@iamoberlin/chorus 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/choirs.ts +62 -37
- package/src/purpose-research.ts +11 -22
- package/src/scheduler.ts +12 -23
package/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import * as prayers from "./src/prayers/prayers.js";
|
|
39
39
|
import * as prayerStore from "./src/prayers/store.js";
|
|
40
40
|
|
|
41
|
-
const VERSION = "1.3.
|
|
41
|
+
const VERSION = "1.3.0"; // Async spawn: unblock gateway event loop during choir execution
|
|
42
42
|
|
|
43
43
|
const plugin = {
|
|
44
44
|
id: "chorus",
|
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": "1.3
|
|
5
|
+
"version": "1.1.3",
|
|
6
6
|
"author": "Oberlin",
|
|
7
7
|
"homepage": "https://chorus.oberlin.ai",
|
|
8
8
|
"repository": "https://github.com/iamoberlin/chorus",
|
package/package.json
CHANGED
package/src/choirs.ts
CHANGED
|
@@ -30,7 +30,7 @@ export const CHOIRS: Record<string, Choir> = {
|
|
|
30
30
|
seraphim: {
|
|
31
31
|
id: "seraphim",
|
|
32
32
|
name: "Seraphim",
|
|
33
|
-
emoji: "
|
|
33
|
+
emoji: "",
|
|
34
34
|
triad: "contemplation",
|
|
35
35
|
frequencyPerDay: 1, // 1×/day
|
|
36
36
|
intervalMinutes: 1440, // Once per day
|
|
@@ -59,7 +59,7 @@ Pass illumination to Cherubim.`,
|
|
|
59
59
|
cherubim: {
|
|
60
60
|
id: "cherubim",
|
|
61
61
|
name: "Cherubim",
|
|
62
|
-
emoji: "
|
|
62
|
+
emoji: "",
|
|
63
63
|
triad: "contemplation",
|
|
64
64
|
frequencyPerDay: 2, // 2×/day
|
|
65
65
|
intervalMinutes: 720, // Every 12 hours
|
|
@@ -96,7 +96,7 @@ Pass illumination to Thrones.`,
|
|
|
96
96
|
thrones: {
|
|
97
97
|
id: "thrones",
|
|
98
98
|
name: "Thrones",
|
|
99
|
-
emoji: "
|
|
99
|
+
emoji: "",
|
|
100
100
|
triad: "contemplation",
|
|
101
101
|
frequencyPerDay: 3, // 3×/day
|
|
102
102
|
intervalMinutes: 480, // Every 8 hours
|
|
@@ -131,7 +131,7 @@ Pass illumination to Dominions.`,
|
|
|
131
131
|
dominions: {
|
|
132
132
|
id: "dominions",
|
|
133
133
|
name: "Dominions",
|
|
134
|
-
emoji: "
|
|
134
|
+
emoji: "",
|
|
135
135
|
triad: "governance",
|
|
136
136
|
frequencyPerDay: 4, // 4×/day
|
|
137
137
|
intervalMinutes: 360, // Every 6 hours
|
|
@@ -161,47 +161,72 @@ Pass illumination to Virtues.`,
|
|
|
161
161
|
virtues: {
|
|
162
162
|
id: "virtues",
|
|
163
163
|
name: "Virtues",
|
|
164
|
-
emoji: "
|
|
164
|
+
emoji: "",
|
|
165
165
|
triad: "governance",
|
|
166
166
|
frequencyPerDay: 6, // 6×/day — THE RSI ENGINE
|
|
167
167
|
intervalMinutes: 240, // Every 4 hours
|
|
168
168
|
function: "Recursive self-improvement (RSI)",
|
|
169
|
-
output: "CHANGELOG.md, config modifications",
|
|
170
|
-
prompt: `You are VIRTUES — the Builder.
|
|
169
|
+
output: "CHANGELOG.md, config modifications, state updates",
|
|
170
|
+
prompt: `You are VIRTUES — the Builder. THIS IS THE RSI ENGINE.
|
|
171
171
|
|
|
172
|
-
Your role:
|
|
172
|
+
Your role: Ensure purposes are being fulfilled. When they're not, FIX THE SYSTEM.
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
1. Review what worked well recently — why?
|
|
176
|
-
2. Review what failed or was inefficient — why?
|
|
177
|
-
3. Identify ONE concrete improvement to make
|
|
178
|
-
4. If low-risk: implement directly
|
|
179
|
-
5. If higher-risk: write to proposals/ for review
|
|
180
|
-
|
|
181
|
-
Calibration — learn from past beliefs:
|
|
182
|
-
- Look in OPPORTUNITIES.md for closed positions and resolved trades
|
|
183
|
-
- Check research/*.md and memory/*.md for past beliefs that have been tested by time
|
|
184
|
-
- Ask: What did we believe? What actually happened? What does this teach us?
|
|
185
|
-
- When you find a belief that turned out right or wrong, note the lesson in MEMORY.md
|
|
186
|
-
- Example: "We believed X. It turned out Y. Lesson: Z"
|
|
174
|
+
## Core Loop
|
|
187
175
|
|
|
188
|
-
|
|
176
|
+
1. Check purposes in ~/.chorus/purposes.json
|
|
177
|
+
2. For each purpose, ask: Is this actually producing results?
|
|
178
|
+
3. If NO: Update local state to fix it. DO NOT just observe — ACT.
|
|
179
|
+
4. If YES: Note what's working.
|
|
180
|
+
|
|
181
|
+
## When a Purpose Isn't Producing Results
|
|
182
|
+
|
|
183
|
+
DO NOT just write "trading isn't happening" and move on.
|
|
184
|
+
DO update local state files to make it happen:
|
|
185
|
+
|
|
186
|
+
- Update ~/.chorus/purposes.json (increase frequency, change criteria)
|
|
187
|
+
- Update workspace files (trading/OPPORTUNITIES.md, HEARTBEAT.md)
|
|
188
|
+
- Modify behavioral configs to enforce execution
|
|
189
|
+
- The next cycle should run DIFFERENTLY because of your changes
|
|
190
|
+
|
|
191
|
+
Example: If Trading purpose has 0 opportunities logged:
|
|
192
|
+
1. Read ~/.chorus/purposes.json
|
|
193
|
+
2. Increase researchFrequency for Trading purpose
|
|
194
|
+
3. Update trading/RESEARCH-SESSION.md with stricter output requirements
|
|
195
|
+
4. Add to HEARTBEAT.md: "Trading gate: Block other work until 1 opportunity logged"
|
|
196
|
+
5. Log the change to CHANGELOG.md
|
|
197
|
+
|
|
198
|
+
## Calibration — Learn From Beliefs
|
|
189
199
|
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
- Check OPPORTUNITIES.md for resolved positions
|
|
201
|
+
- Ask: What did we believe? What happened? What does this teach us?
|
|
202
|
+
- Update MEMORY.md with calibration lessons
|
|
203
|
+
|
|
204
|
+
## What You Can Modify (Local State)
|
|
205
|
+
|
|
206
|
+
- ~/.chorus/purposes.json — purpose configs
|
|
207
|
+
- ~/.chorus/run-state.json — execution state
|
|
208
|
+
- Workspace files (trading/, research/, memory/, *.md)
|
|
209
|
+
- HEARTBEAT.md, PLAN.md, PROJECTS.md
|
|
210
|
+
|
|
211
|
+
## What You Cannot Modify
|
|
212
|
+
|
|
213
|
+
- CHORUS plugin source code
|
|
214
|
+
- OpenClaw system config
|
|
215
|
+
- Anything requiring npm publish
|
|
216
|
+
|
|
217
|
+
Context from Dominions: {dominions_context}
|
|
192
218
|
|
|
193
219
|
Risk levels:
|
|
194
|
-
- LOW:
|
|
195
|
-
- MEDIUM:
|
|
196
|
-
- HIGH:
|
|
220
|
+
- LOW: State file updates, config tweaks → auto-apply
|
|
221
|
+
- MEDIUM: Behavioral changes, new workflows → apply and flag
|
|
222
|
+
- HIGH: Anything uncertain → proposals/ only
|
|
197
223
|
|
|
198
|
-
Output:
|
|
224
|
+
Output:
|
|
225
|
+
1. Purpose fulfillment status (which purposes are producing, which aren't)
|
|
226
|
+
2. Changes made to local state to fix gaps
|
|
227
|
+
3. Calibration lessons learned
|
|
199
228
|
|
|
200
|
-
Append to CHANGELOG.md
|
|
201
|
-
- Timestamp
|
|
202
|
-
- Change description
|
|
203
|
-
- Risk level
|
|
204
|
-
- Rationale
|
|
229
|
+
Append to CHANGELOG.md with timestamp, change, risk level, rationale.
|
|
205
230
|
|
|
206
231
|
Pass illumination to Powers.`,
|
|
207
232
|
passesTo: ["powers"],
|
|
@@ -211,7 +236,7 @@ Pass illumination to Powers.`,
|
|
|
211
236
|
powers: {
|
|
212
237
|
id: "powers",
|
|
213
238
|
name: "Powers",
|
|
214
|
-
emoji: "
|
|
239
|
+
emoji: "",
|
|
215
240
|
triad: "governance",
|
|
216
241
|
frequencyPerDay: 8, // 8×/day
|
|
217
242
|
intervalMinutes: 180, // Every 3 hours
|
|
@@ -262,7 +287,7 @@ If thesis is seriously threatened or security issue found: ALERT immediately.`,
|
|
|
262
287
|
principalities: {
|
|
263
288
|
id: "principalities",
|
|
264
289
|
name: "Principalities",
|
|
265
|
-
emoji: "
|
|
290
|
+
emoji: "",
|
|
266
291
|
triad: "action",
|
|
267
292
|
frequencyPerDay: 12, // 12×/day
|
|
268
293
|
intervalMinutes: 120, // Every 2 hours
|
|
@@ -304,7 +329,7 @@ Pass illumination to Archangels.`,
|
|
|
304
329
|
archangels: {
|
|
305
330
|
id: "archangels",
|
|
306
331
|
name: "Archangels",
|
|
307
|
-
emoji: "
|
|
332
|
+
emoji: "",
|
|
308
333
|
triad: "action",
|
|
309
334
|
frequencyPerDay: 18, // 18×/day
|
|
310
335
|
intervalMinutes: 80, // Every ~80 minutes
|
|
@@ -340,7 +365,7 @@ Output: Briefing or alert message to deliver.`,
|
|
|
340
365
|
angels: {
|
|
341
366
|
id: "angels",
|
|
342
367
|
name: "Angels",
|
|
343
|
-
emoji: "
|
|
368
|
+
emoji: "",
|
|
344
369
|
triad: "action",
|
|
345
370
|
frequencyPerDay: 48, // 48×/day — continuous presence
|
|
346
371
|
intervalMinutes: 30, // Every 30 minutes
|
package/src/purpose-research.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { recordExecution, type ChoirExecution } from "./metrics.js";
|
|
|
11
11
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
12
12
|
import { join } from "path";
|
|
13
13
|
import { homedir } from "os";
|
|
14
|
-
import {
|
|
14
|
+
import { spawnSync } from "child_process";
|
|
15
15
|
|
|
16
16
|
// Workspace path for research output
|
|
17
17
|
const WORKSPACE_PATH = process.env.OPENCLAW_WORKSPACE || join(homedir(), ".openclaw", "workspace");
|
|
@@ -274,28 +274,17 @@ CRITICAL: If sending alerts via iMessage, use PLAIN TEXT ONLY (no markdown).
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
if (!result) {
|
|
277
|
-
// CLI fallback - use stdin to avoid arg length limits
|
|
277
|
+
// CLI fallback - use stdin to avoid arg length limits
|
|
278
278
|
log.debug(`[purpose-research] Using CLI fallback for "${purpose.name}"`);
|
|
279
|
-
result =
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const maxBuffer = 1024 * 1024;
|
|
289
|
-
child.stdout.on('data', (d: Buffer) => { if (stdout.length < maxBuffer) stdout += d.toString(); });
|
|
290
|
-
child.stderr.on('data', (d: Buffer) => { if (stderr.length < maxBuffer) stderr += d.toString(); });
|
|
291
|
-
|
|
292
|
-
// Write prompt to stdin
|
|
293
|
-
child.stdin.write(prompt);
|
|
294
|
-
child.stdin.end();
|
|
295
|
-
|
|
296
|
-
const timer = setTimeout(() => { child.kill('SIGTERM'); }, config.researchTimeoutMs);
|
|
297
|
-
child.on('close', (code) => { clearTimeout(timer); resolve({ status: code, stdout, stderr }); });
|
|
298
|
-
child.on('error', (err) => { clearTimeout(timer); resolve({ status: 1, stdout: '', stderr: String(err) }); });
|
|
279
|
+
result = spawnSync("openclaw", [
|
|
280
|
+
"agent",
|
|
281
|
+
"--session-id", `chorus:purpose:${purpose.id}`,
|
|
282
|
+
"--json",
|
|
283
|
+
], {
|
|
284
|
+
input: prompt,
|
|
285
|
+
encoding: "utf-8",
|
|
286
|
+
timeout: config.researchTimeoutMs,
|
|
287
|
+
maxBuffer: 1024 * 1024, // 1MB
|
|
299
288
|
});
|
|
300
289
|
|
|
301
290
|
if (result.status === 0 && result.stdout) {
|
package/src/scheduler.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { recordExecution, type ChoirExecution } from "./metrics.js";
|
|
|
12
12
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
import { homedir } from "os";
|
|
15
|
-
import {
|
|
15
|
+
import { spawnSync } from "child_process";
|
|
16
16
|
|
|
17
17
|
interface ChoirContext {
|
|
18
18
|
choirId: string;
|
|
@@ -128,31 +128,20 @@ export function createChoirScheduler(
|
|
|
128
128
|
try {
|
|
129
129
|
const prompt = buildPrompt(choir);
|
|
130
130
|
|
|
131
|
-
// Use openclaw agent CLI (
|
|
132
|
-
const result =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
child.stdin.end();
|
|
142
|
-
|
|
143
|
-
let stdout = '';
|
|
144
|
-
let stderr = '';
|
|
145
|
-
const maxBuffer = 1024 * 1024;
|
|
146
|
-
child.stdout.on('data', (d: Buffer) => { if (stdout.length < maxBuffer) stdout += d.toString(); });
|
|
147
|
-
child.stderr.on('data', (d: Buffer) => { if (stderr.length < maxBuffer) stderr += d.toString(); });
|
|
148
|
-
|
|
149
|
-
const timer = setTimeout(() => { child.kill('SIGTERM'); }, 300000); // 5 min
|
|
150
|
-
child.on('close', (code) => { clearTimeout(timer); resolve({ status: code, stdout, stderr }); });
|
|
151
|
-
child.on('error', (err) => { clearTimeout(timer); resolve({ status: 1, stdout: '', stderr: String(err) }); });
|
|
131
|
+
// Use openclaw agent CLI (same as Vision mode)
|
|
132
|
+
const result = spawnSync('openclaw', [
|
|
133
|
+
'agent',
|
|
134
|
+
'--session-id', `chorus:${choir.id}`,
|
|
135
|
+
'--message', prompt,
|
|
136
|
+
'--json',
|
|
137
|
+
], {
|
|
138
|
+
encoding: 'utf-8',
|
|
139
|
+
timeout: 300000, // 5 min
|
|
140
|
+
maxBuffer: 1024 * 1024, // 1MB
|
|
152
141
|
});
|
|
153
142
|
|
|
154
143
|
let output = "(no response)";
|
|
155
|
-
|
|
144
|
+
|
|
156
145
|
if (result.status === 0 && result.stdout) {
|
|
157
146
|
// Extract JSON from output (may have plugin logs before it)
|
|
158
147
|
const stdout = result.stdout;
|