@iamoberlin/chorus 2.2.1 → 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 +6 -0
- package/index.ts +39 -79
- package/openclaw.plugin.json +74 -1
- package/package.json +1 -1
- package/src/choirs.ts +51 -23
- 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/purpose-research.ts +78 -24
- package/src/scheduler.ts +88 -66
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**:
|
package/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ 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
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) {
|
|
@@ -130,68 +121,6 @@ const plugin = {
|
|
|
130
121
|
return '';
|
|
131
122
|
}
|
|
132
123
|
|
|
133
|
-
// Deliver choir output to user via OpenClaw messaging (channel-agnostic)
|
|
134
|
-
// Reads target from OpenClaw config (channels.*.allowFrom) — no hardcoded PII
|
|
135
|
-
function deliverIfNeeded(choir: typeof CHOIRS[string], text: string): void {
|
|
136
|
-
if (!choir.delivers || !text || text === 'HEARTBEAT_OK' || text === 'NO_REPLY') return;
|
|
137
|
-
|
|
138
|
-
// Resolve delivery target from OpenClaw channel config
|
|
139
|
-
const channels = api.config?.channels as Record<string, any> | undefined;
|
|
140
|
-
let target: string | undefined;
|
|
141
|
-
let channel: string | undefined;
|
|
142
|
-
|
|
143
|
-
if (channels) {
|
|
144
|
-
for (const [ch, cfg] of Object.entries(channels)) {
|
|
145
|
-
if (cfg?.enabled && cfg?.allowFrom?.[0]) {
|
|
146
|
-
target = cfg.allowFrom[0];
|
|
147
|
-
channel = ch;
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (!target) {
|
|
154
|
-
console.log(` ⚠ No delivery target found in OpenClaw config`);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Strip markdown for channels that don't support it
|
|
159
|
-
let deliveryText = text.slice(0, 4000);
|
|
160
|
-
if (channel === 'imessage') {
|
|
161
|
-
deliveryText = deliveryText
|
|
162
|
-
.replace(/\*\*(.+?)\*\*/g, '$1') // bold
|
|
163
|
-
.replace(/\*(.+?)\*/g, '$1') // italic
|
|
164
|
-
.replace(/__(.+?)__/g, '$1') // bold alt
|
|
165
|
-
.replace(/_(.+?)_/g, '$1') // italic alt
|
|
166
|
-
.replace(/`(.+?)`/g, '$1') // inline code
|
|
167
|
-
.replace(/```[\s\S]*?```/g, '') // code blocks
|
|
168
|
-
.replace(/^#{1,6}\s+/gm, '') // headers
|
|
169
|
-
.replace(/^\s*[-*+]\s+/gm, '• ') // bullet lists
|
|
170
|
-
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1'); // links
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
const args = [
|
|
175
|
-
'message', 'send',
|
|
176
|
-
'--target', target,
|
|
177
|
-
'--message', deliveryText,
|
|
178
|
-
];
|
|
179
|
-
if (channel) args.push('--channel', channel);
|
|
180
|
-
|
|
181
|
-
const deliveryResult = spawnSync('openclaw', args, {
|
|
182
|
-
encoding: 'utf-8',
|
|
183
|
-
timeout: 30000,
|
|
184
|
-
});
|
|
185
|
-
if (deliveryResult.status === 0) {
|
|
186
|
-
console.log(` 📨 Delivered to user via ${channel || 'default'}`);
|
|
187
|
-
} else {
|
|
188
|
-
console.log(` ⚠ Delivery failed: ${(deliveryResult.stderr || '').slice(0, 80)}`);
|
|
189
|
-
}
|
|
190
|
-
} catch (err: any) {
|
|
191
|
-
console.log(` ⚠ Delivery error: ${(err.message || '').slice(0, 80)}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
124
|
// Register CLI
|
|
196
125
|
api.registerCli((ctx) => {
|
|
197
126
|
const program = ctx.program.command("chorus").description("CHORUS Nine Choirs management");
|
|
@@ -315,7 +244,7 @@ const plugin = {
|
|
|
315
244
|
const preview = text.slice(0, 150).replace(/\n/g, ' ');
|
|
316
245
|
console.log(` ${preview}${text.length > 150 ? '...' : ''}`);
|
|
317
246
|
}
|
|
318
|
-
|
|
247
|
+
await deliverChoirOutput(api, choir, text, api.logger);
|
|
319
248
|
} catch (err) {
|
|
320
249
|
console.error(` ✗ ${choir.name} failed:`, err);
|
|
321
250
|
}
|
|
@@ -341,7 +270,7 @@ const plugin = {
|
|
|
341
270
|
const preview = text.slice(0, 150).replace(/\n/g, ' ');
|
|
342
271
|
console.log(` ${preview}${text.length > 150 ? '...' : ''}`);
|
|
343
272
|
}
|
|
344
|
-
|
|
273
|
+
await deliverChoirOutput(api, choir, text, api.logger);
|
|
345
274
|
} else {
|
|
346
275
|
const errMsg = result.stderr || result.stdout || 'Unknown error';
|
|
347
276
|
if (errMsg.includes('ECONNREFUSED') || errMsg.includes('connect')) {
|
|
@@ -483,7 +412,7 @@ const plugin = {
|
|
|
483
412
|
console.log(` ✓`);
|
|
484
413
|
|
|
485
414
|
// Deliver output to user via OpenClaw messaging if choir is marked for delivery
|
|
486
|
-
|
|
415
|
+
void deliverChoirOutput(api, choir, text, api.logger);
|
|
487
416
|
} catch {
|
|
488
417
|
const fallback = result.stdout?.slice(-2000) || `[${choir.name} completed]`;
|
|
489
418
|
contextStore.set(choirId, fallback);
|
|
@@ -684,6 +613,37 @@ const plugin = {
|
|
|
684
613
|
}
|
|
685
614
|
});
|
|
686
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
|
+
|
|
687
647
|
// Daemon commands
|
|
688
648
|
const daemonCmd = program.command("daemon").description("Autonomous attention daemon");
|
|
689
649
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "chorus",
|
|
3
3
|
"name": "CHORUS",
|
|
4
4
|
"description": "CHORUS: Hierarchy Of Recursive Unified Self-improvement",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.3.0",
|
|
6
6
|
"author": "Oberlin",
|
|
7
7
|
"homepage": "https://chorus.oberlin.ai",
|
|
8
8
|
"repository": "https://github.com/iamoberlin/chorus",
|
|
@@ -48,10 +48,40 @@
|
|
|
48
48
|
"type": "boolean",
|
|
49
49
|
"description": "Enable the daemon"
|
|
50
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
|
+
},
|
|
51
69
|
"thinkThreshold": {
|
|
52
70
|
"type": "number",
|
|
53
71
|
"description": "Minimum priority to invoke cognition (0-100)"
|
|
54
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
|
+
},
|
|
55
85
|
"quietHoursStart": {
|
|
56
86
|
"type": "number",
|
|
57
87
|
"description": "Hour to start quiet mode (0-23)"
|
|
@@ -81,6 +111,44 @@
|
|
|
81
111
|
"defaultMaxFrequency": {
|
|
82
112
|
"type": "number",
|
|
83
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"
|
|
84
152
|
}
|
|
85
153
|
}
|
|
86
154
|
}
|
|
@@ -118,6 +186,11 @@
|
|
|
118
186
|
"purposeResearch": {
|
|
119
187
|
"label": "Purpose Research",
|
|
120
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)"
|
|
121
194
|
}
|
|
122
195
|
}
|
|
123
196
|
}
|
package/package.json
CHANGED
package/src/choirs.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const CHOIRS: Record<string, Choir> = {
|
|
|
36
36
|
frequencyPerDay: 1, // 1×/day
|
|
37
37
|
intervalMinutes: 1440, // Once per day
|
|
38
38
|
function: "Mission clarity and purpose",
|
|
39
|
-
output: "
|
|
39
|
+
output: "Mission assessment",
|
|
40
40
|
prompt: `You are SERAPHIM — the Mission Keeper.
|
|
41
41
|
|
|
42
42
|
Your role: Ensure the mission remains true and aligned. Burn away drift.
|
|
@@ -49,7 +49,10 @@ Core questions:
|
|
|
49
49
|
|
|
50
50
|
Read SOUL.md, USER.md, and MEMORY.md for context.
|
|
51
51
|
|
|
52
|
-
|
|
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.
|
|
53
56
|
If mission is clear and unchanged, simply confirm alignment.
|
|
54
57
|
|
|
55
58
|
Pass illumination to Cherubim.`,
|
|
@@ -103,13 +106,13 @@ Pass illumination to Thrones.`,
|
|
|
103
106
|
frequencyPerDay: 3, // 3×/day
|
|
104
107
|
intervalMinutes: 480, // Every 8 hours
|
|
105
108
|
function: "Judgment and prioritization",
|
|
106
|
-
output: "
|
|
109
|
+
output: "Priority decisions",
|
|
107
110
|
prompt: `You are THRONES — the Judgment Bearer.
|
|
108
111
|
|
|
109
112
|
Your role: Decide priorities and allocate focus ruthlessly.
|
|
110
113
|
|
|
111
114
|
Tasks:
|
|
112
|
-
1. Review current priorities in
|
|
115
|
+
1. Review current priorities in PROJECTS.md
|
|
113
116
|
2. Assess what's working and what isn't
|
|
114
117
|
3. Decide what to focus on next
|
|
115
118
|
4. Identify what to say NO to — what to kill
|
|
@@ -119,7 +122,7 @@ Context from Cherubim: {cherubim_context}
|
|
|
119
122
|
|
|
120
123
|
Output: Updated priorities (max 3 focus areas). What we're NOT doing. Any escalations.
|
|
121
124
|
|
|
122
|
-
Update
|
|
125
|
+
Update PROJECTS.md if priorities changed.
|
|
123
126
|
Pass illumination to Dominions.`,
|
|
124
127
|
passesTo: ["dominions"],
|
|
125
128
|
receivesFrom: ["cherubim"],
|
|
@@ -182,24 +185,24 @@ Your role: Ensure purposes are being fulfilled. When they're not, FIX THE SYSTEM
|
|
|
182
185
|
|
|
183
186
|
## When a Purpose Isn't Producing Results
|
|
184
187
|
|
|
185
|
-
DO NOT just write "
|
|
188
|
+
DO NOT just write "a purpose isn't happening" and move on.
|
|
186
189
|
DO update local state files to make it happen:
|
|
187
190
|
|
|
188
191
|
- Update ~/.chorus/purposes.json (increase frequency, change criteria)
|
|
189
|
-
- Update workspace files (
|
|
192
|
+
- Update relevant workspace files (research notes, summaries, HEARTBEAT.md)
|
|
190
193
|
- Modify behavioral configs to enforce execution
|
|
191
194
|
- The next cycle should run DIFFERENTLY because of your changes
|
|
192
195
|
|
|
193
|
-
Example: If
|
|
196
|
+
Example: If a purpose has 0 concrete outputs logged:
|
|
194
197
|
1. Read ~/.chorus/purposes.json
|
|
195
|
-
2. Increase researchFrequency for
|
|
196
|
-
3. Update
|
|
197
|
-
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"
|
|
198
201
|
5. Log the change to CHANGELOG.md
|
|
199
202
|
|
|
200
203
|
## Calibration — Learn From Beliefs
|
|
201
204
|
|
|
202
|
-
- Check
|
|
205
|
+
- Check resolved outcomes for prior predictions or decisions
|
|
203
206
|
- Ask: What did we believe? What happened? What does this teach us?
|
|
204
207
|
- Update MEMORY.md with calibration lessons
|
|
205
208
|
|
|
@@ -207,8 +210,8 @@ Example: If Trading purpose has 0 opportunities logged:
|
|
|
207
210
|
|
|
208
211
|
- ~/.chorus/purposes.json — purpose configs
|
|
209
212
|
- ~/.chorus/run-state.json — execution state
|
|
210
|
-
- Workspace files (
|
|
211
|
-
- HEARTBEAT.md,
|
|
213
|
+
- Workspace files (research/, memory/, notes/, *.md)
|
|
214
|
+
- HEARTBEAT.md, PROJECTS.md
|
|
212
215
|
|
|
213
216
|
## What You Cannot Modify
|
|
214
217
|
|
|
@@ -216,6 +219,12 @@ Example: If Trading purpose has 0 opportunities logged:
|
|
|
216
219
|
- OpenClaw system config
|
|
217
220
|
- Anything requiring npm publish
|
|
218
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
|
+
|
|
219
228
|
Context from Dominions: {dominions_context}
|
|
220
229
|
|
|
221
230
|
Risk levels:
|
|
@@ -264,7 +273,7 @@ Red-team protocol:
|
|
|
264
273
|
- What are we avoiding looking at?
|
|
265
274
|
|
|
266
275
|
Challenge our beliefs:
|
|
267
|
-
- Look in
|
|
276
|
+
- Look in research/*.md, memory/*.md, and planning docs for stated beliefs
|
|
268
277
|
- Find claims like "I believe X will happen" or "This suggests Y"
|
|
269
278
|
- Ask: What would make this wrong? What are we missing?
|
|
270
279
|
- If a belief looks shaky, say so clearly
|
|
@@ -275,9 +284,16 @@ SECURITY FOCUS:
|
|
|
275
284
|
- Check for persona drift or identity erosion
|
|
276
285
|
- Validate system prompt integrity
|
|
277
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
|
+
|
|
278
294
|
Output: Challenges to current thinking. Beliefs that look weak. Risks identified. Recommendations.
|
|
279
295
|
|
|
280
|
-
If
|
|
296
|
+
If a core assumption is seriously threatened or a security issue is found: ALERT immediately.`,
|
|
281
297
|
passesTo: ["principalities"],
|
|
282
298
|
receivesFrom: ["virtues"],
|
|
283
299
|
},
|
|
@@ -302,7 +318,7 @@ Your role: Research and monitor the domains that matter.
|
|
|
302
318
|
|
|
303
319
|
Domains to cover (rotate through):
|
|
304
320
|
- AI Industry: Companies, funding, regulation, breakthroughs
|
|
305
|
-
-
|
|
321
|
+
- Environment: External signals relevant to active purposes
|
|
306
322
|
- Competitors: Developments from players in our space
|
|
307
323
|
- Tools: New capabilities, skills, or integrations available
|
|
308
324
|
|
|
@@ -311,6 +327,14 @@ Tasks:
|
|
|
311
327
|
2. Assess relevance to our projects
|
|
312
328
|
3. Flag anything urgent for Archangels
|
|
313
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.
|
|
314
338
|
|
|
315
339
|
When you find something significant, state what you believe will happen:
|
|
316
340
|
- "I believe X will happen by [timeframe] because..."
|
|
@@ -341,26 +365,30 @@ Pass illumination to Archangels.`,
|
|
|
341
365
|
delivers: true, // Output routed to user via OpenClaw messaging
|
|
342
366
|
prompt: `You are ARCHANGELS — the Herald.
|
|
343
367
|
|
|
344
|
-
Your role: Produce briefings and deliver them
|
|
368
|
+
Your role: Produce briefings and deliver them through configured channels.
|
|
345
369
|
|
|
346
370
|
Briefing types:
|
|
347
|
-
- Morning (6-9 AM
|
|
348
|
-
- 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
|
|
349
373
|
- Alert: Time-sensitive information requiring attention
|
|
350
|
-
- Update: Regular
|
|
374
|
+
- Update: Regular status when conditions change
|
|
351
375
|
|
|
352
376
|
Alert criteria (send immediately):
|
|
353
|
-
-
|
|
377
|
+
- Core purpose assumption challenged
|
|
354
378
|
- Time-sensitive opportunity
|
|
355
379
|
- Urgent calendar/email
|
|
356
380
|
- Security concern from Powers
|
|
357
381
|
|
|
358
382
|
Context from Principalities: {principalities_context}
|
|
359
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
|
+
|
|
360
388
|
RULES:
|
|
361
389
|
- ALWAYS produce a briefing. Never return HEARTBEAT_OK or NO_REPLY.
|
|
362
390
|
- Be concise — headlines, not essays.
|
|
363
|
-
- Morning briefings should include
|
|
391
|
+
- Morning briefings should include priorities, schedule constraints, and key catalysts.
|
|
364
392
|
- If nothing is urgent, still produce a status update.
|
|
365
393
|
- During quiet hours (11 PM - 7 AM ET), only deliver truly urgent alerts.
|
|
366
394
|
|
package/src/config.ts
CHANGED
|
@@ -20,6 +20,22 @@ export interface ChorusConfig {
|
|
|
20
20
|
dailyRunCap: number;
|
|
21
21
|
defaultFrequency: number;
|
|
22
22
|
defaultMaxFrequency: number;
|
|
23
|
+
researchTimeoutMs: number;
|
|
24
|
+
checkIntervalMs: number;
|
|
25
|
+
};
|
|
26
|
+
daemon: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
senses: {
|
|
29
|
+
inbox: boolean;
|
|
30
|
+
purposes: boolean;
|
|
31
|
+
time: boolean;
|
|
32
|
+
};
|
|
33
|
+
thinkThreshold: number;
|
|
34
|
+
pollIntervalMs: number;
|
|
35
|
+
minSleepMs: number;
|
|
36
|
+
maxSleepMs: number;
|
|
37
|
+
quietHoursStart: number;
|
|
38
|
+
quietHoursEnd: number;
|
|
23
39
|
};
|
|
24
40
|
prayers: {
|
|
25
41
|
enabled: boolean;
|
|
@@ -45,6 +61,23 @@ export interface ChorusPluginConfig {
|
|
|
45
61
|
dailyRunCap?: number;
|
|
46
62
|
defaultFrequency?: number;
|
|
47
63
|
defaultMaxFrequency?: number;
|
|
64
|
+
researchTimeoutMs?: number;
|
|
65
|
+
checkIntervalMs?: number;
|
|
66
|
+
};
|
|
67
|
+
/** Daemon config */
|
|
68
|
+
daemon?: {
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
senses?: {
|
|
71
|
+
inbox?: boolean;
|
|
72
|
+
purposes?: boolean;
|
|
73
|
+
time?: boolean;
|
|
74
|
+
};
|
|
75
|
+
thinkThreshold?: number;
|
|
76
|
+
pollIntervalMs?: number;
|
|
77
|
+
minSleepMs?: number;
|
|
78
|
+
maxSleepMs?: number;
|
|
79
|
+
quietHoursStart?: number;
|
|
80
|
+
quietHoursEnd?: number;
|
|
48
81
|
};
|
|
49
82
|
/** On-chain prayer config */
|
|
50
83
|
prayers?: {
|
|
@@ -72,6 +105,22 @@ const DEFAULT_CONFIG: ChorusConfig = {
|
|
|
72
105
|
dailyRunCap: 50,
|
|
73
106
|
defaultFrequency: 6,
|
|
74
107
|
defaultMaxFrequency: 24,
|
|
108
|
+
researchTimeoutMs: 300000,
|
|
109
|
+
checkIntervalMs: 60000,
|
|
110
|
+
},
|
|
111
|
+
daemon: {
|
|
112
|
+
enabled: true,
|
|
113
|
+
senses: {
|
|
114
|
+
inbox: true,
|
|
115
|
+
purposes: true,
|
|
116
|
+
time: true,
|
|
117
|
+
},
|
|
118
|
+
thinkThreshold: 55,
|
|
119
|
+
pollIntervalMs: 5 * 60 * 1000,
|
|
120
|
+
minSleepMs: 30 * 1000,
|
|
121
|
+
maxSleepMs: 10 * 60 * 1000,
|
|
122
|
+
quietHoursStart: 23,
|
|
123
|
+
quietHoursEnd: 7,
|
|
75
124
|
},
|
|
76
125
|
prayers: {
|
|
77
126
|
enabled: true,
|
|
@@ -146,6 +195,48 @@ export function loadChorusConfig(pluginConfig?: ChorusPluginConfig): ChorusConfi
|
|
|
146
195
|
if (pluginConfig.purposeResearch.defaultMaxFrequency !== undefined) {
|
|
147
196
|
config.purposeResearch.defaultMaxFrequency = pluginConfig.purposeResearch.defaultMaxFrequency;
|
|
148
197
|
}
|
|
198
|
+
if (pluginConfig.purposeResearch.researchTimeoutMs !== undefined) {
|
|
199
|
+
config.purposeResearch.researchTimeoutMs = pluginConfig.purposeResearch.researchTimeoutMs;
|
|
200
|
+
}
|
|
201
|
+
if (pluginConfig.purposeResearch.checkIntervalMs !== undefined) {
|
|
202
|
+
config.purposeResearch.checkIntervalMs = pluginConfig.purposeResearch.checkIntervalMs;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Daemon
|
|
207
|
+
if (pluginConfig.daemon) {
|
|
208
|
+
if (pluginConfig.daemon.enabled !== undefined) {
|
|
209
|
+
config.daemon.enabled = pluginConfig.daemon.enabled;
|
|
210
|
+
}
|
|
211
|
+
if (pluginConfig.daemon.senses) {
|
|
212
|
+
if (pluginConfig.daemon.senses.inbox !== undefined) {
|
|
213
|
+
config.daemon.senses.inbox = pluginConfig.daemon.senses.inbox;
|
|
214
|
+
}
|
|
215
|
+
if (pluginConfig.daemon.senses.purposes !== undefined) {
|
|
216
|
+
config.daemon.senses.purposes = pluginConfig.daemon.senses.purposes;
|
|
217
|
+
}
|
|
218
|
+
if (pluginConfig.daemon.senses.time !== undefined) {
|
|
219
|
+
config.daemon.senses.time = pluginConfig.daemon.senses.time;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (pluginConfig.daemon.thinkThreshold !== undefined) {
|
|
223
|
+
config.daemon.thinkThreshold = pluginConfig.daemon.thinkThreshold;
|
|
224
|
+
}
|
|
225
|
+
if (pluginConfig.daemon.pollIntervalMs !== undefined) {
|
|
226
|
+
config.daemon.pollIntervalMs = pluginConfig.daemon.pollIntervalMs;
|
|
227
|
+
}
|
|
228
|
+
if (pluginConfig.daemon.minSleepMs !== undefined) {
|
|
229
|
+
config.daemon.minSleepMs = pluginConfig.daemon.minSleepMs;
|
|
230
|
+
}
|
|
231
|
+
if (pluginConfig.daemon.maxSleepMs !== undefined) {
|
|
232
|
+
config.daemon.maxSleepMs = pluginConfig.daemon.maxSleepMs;
|
|
233
|
+
}
|
|
234
|
+
if (pluginConfig.daemon.quietHoursStart !== undefined) {
|
|
235
|
+
config.daemon.quietHoursStart = pluginConfig.daemon.quietHoursStart;
|
|
236
|
+
}
|
|
237
|
+
if (pluginConfig.daemon.quietHoursEnd !== undefined) {
|
|
238
|
+
config.daemon.quietHoursEnd = pluginConfig.daemon.quietHoursEnd;
|
|
239
|
+
}
|
|
149
240
|
}
|
|
150
241
|
|
|
151
242
|
return config;
|