openclaw-node-harness 2.0.4 → 2.1.1
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 +646 -3
- package/bin/hyperagent.mjs +419 -0
- package/bin/lane-watchdog.js +23 -2
- package/bin/mesh-agent.js +439 -28
- package/bin/mesh-bridge.js +69 -3
- package/bin/mesh-health-publisher.js +41 -1
- package/bin/mesh-task-daemon.js +821 -26
- package/bin/mesh.js +411 -20
- package/config/claude-settings.json +95 -0
- package/config/daemon.json.template +2 -1
- package/config/git-hooks/pre-commit +13 -0
- package/config/git-hooks/pre-push +12 -0
- package/config/harness-rules.json +174 -0
- package/config/plan-templates/team-bugfix.yaml +52 -0
- package/config/plan-templates/team-deploy.yaml +50 -0
- package/config/plan-templates/team-feature.yaml +71 -0
- package/config/roles/qa-engineer.yaml +36 -0
- package/config/roles/solidity-dev.yaml +51 -0
- package/config/roles/tech-architect.yaml +36 -0
- package/config/rules/framework/solidity.md +22 -0
- package/config/rules/framework/typescript.md +21 -0
- package/config/rules/framework/unity.md +21 -0
- package/config/rules/universal/design-docs.md +18 -0
- package/config/rules/universal/git-hygiene.md +18 -0
- package/config/rules/universal/security.md +19 -0
- package/config/rules/universal/test-standards.md +19 -0
- package/identity/DELEGATION.md +6 -6
- package/install.sh +296 -10
- package/lib/agent-activity.js +2 -2
- package/lib/circling-parser.js +119 -0
- package/lib/exec-safety.js +105 -0
- package/lib/hyperagent-store.mjs +652 -0
- package/lib/kanban-io.js +24 -31
- package/lib/llm-providers.js +16 -0
- package/lib/mcp-knowledge/bench.mjs +118 -0
- package/lib/mcp-knowledge/core.mjs +530 -0
- package/lib/mcp-knowledge/package.json +25 -0
- package/lib/mcp-knowledge/server.mjs +252 -0
- package/lib/mcp-knowledge/test.mjs +802 -0
- package/lib/memory-budget.mjs +261 -0
- package/lib/mesh-collab.js +483 -165
- package/lib/mesh-harness.js +427 -0
- package/lib/mesh-plans.js +79 -50
- package/lib/mesh-tasks.js +132 -49
- package/lib/nats-resolve.js +4 -4
- package/lib/plan-templates.js +226 -0
- package/lib/pre-compression-flush.mjs +322 -0
- package/lib/role-loader.js +292 -0
- package/lib/rule-loader.js +358 -0
- package/lib/session-store.mjs +461 -0
- package/lib/transcript-parser.mjs +292 -0
- package/mission-control/drizzle/soul_schema_update.sql +29 -0
- package/mission-control/drizzle.config.ts +1 -4
- package/mission-control/package-lock.json +1571 -83
- package/mission-control/package.json +6 -2
- package/mission-control/scripts/gen-chronology.js +3 -3
- package/mission-control/scripts/import-pipeline-v2.js +0 -16
- package/mission-control/scripts/import-pipeline.js +0 -15
- package/mission-control/src/app/api/cowork/clusters/[id]/members/route.ts +117 -0
- package/mission-control/src/app/api/cowork/clusters/[id]/route.ts +84 -0
- package/mission-control/src/app/api/cowork/clusters/route.ts +141 -0
- package/mission-control/src/app/api/cowork/dispatch/route.ts +128 -0
- package/mission-control/src/app/api/cowork/events/route.ts +65 -0
- package/mission-control/src/app/api/cowork/intervene/route.ts +259 -0
- package/mission-control/src/app/api/cowork/sessions/[id]/route.ts +37 -0
- package/mission-control/src/app/api/cowork/sessions/route.ts +64 -0
- package/mission-control/src/app/api/diagnostics/route.ts +97 -0
- package/mission-control/src/app/api/diagnostics/test-runner/route.ts +990 -0
- package/mission-control/src/app/api/memory/search/route.ts +6 -3
- package/mission-control/src/app/api/mesh/events/route.ts +95 -19
- package/mission-control/src/app/api/mesh/identity/route.ts +11 -0
- package/mission-control/src/app/api/mesh/tasks/[id]/route.ts +92 -0
- package/mission-control/src/app/api/mesh/tasks/route.ts +91 -0
- package/mission-control/src/app/api/souls/[id]/evolution/route.ts +21 -5
- package/mission-control/src/app/api/souls/[id]/prompt/route.ts +7 -1
- package/mission-control/src/app/api/souls/[id]/propagate/route.ts +14 -2
- package/mission-control/src/app/api/tasks/[id]/handoff/route.ts +8 -2
- package/mission-control/src/app/api/tasks/[id]/route.ts +90 -4
- package/mission-control/src/app/api/tasks/route.ts +21 -30
- package/mission-control/src/app/api/workspace/read/route.ts +11 -0
- package/mission-control/src/app/cowork/page.tsx +261 -0
- package/mission-control/src/app/diagnostics/page.tsx +385 -0
- package/mission-control/src/app/graph/page.tsx +26 -0
- package/mission-control/src/app/memory/page.tsx +1 -1
- package/mission-control/src/app/obsidian/page.tsx +36 -6
- package/mission-control/src/app/roadmap/page.tsx +24 -0
- package/mission-control/src/app/souls/page.tsx +2 -2
- package/mission-control/src/components/board/execution-config.tsx +431 -0
- package/mission-control/src/components/board/kanban-board.tsx +75 -9
- package/mission-control/src/components/board/kanban-column.tsx +135 -19
- package/mission-control/src/components/board/task-card.tsx +55 -2
- package/mission-control/src/components/board/unified-task-dialog.tsx +82 -4
- package/mission-control/src/components/cowork/cluster-card.tsx +176 -0
- package/mission-control/src/components/cowork/create-cluster-dialog.tsx +251 -0
- package/mission-control/src/components/cowork/dispatch-form.tsx +423 -0
- package/mission-control/src/components/cowork/role-picker.tsx +102 -0
- package/mission-control/src/components/cowork/session-card.tsx +284 -0
- package/mission-control/src/components/layout/sidebar.tsx +39 -2
- package/mission-control/src/lib/__tests__/daily-log.test.ts +82 -0
- package/mission-control/src/lib/__tests__/memory-md.test.ts +87 -0
- package/mission-control/src/lib/__tests__/mesh-kv-sync.test.ts +465 -0
- package/mission-control/src/lib/__tests__/mocks/mock-kv.ts +131 -0
- package/mission-control/src/lib/__tests__/status-kanban.test.ts +46 -0
- package/mission-control/src/lib/__tests__/task-markdown.test.ts +188 -0
- package/mission-control/src/lib/__tests__/wikilinks.test.ts +175 -0
- package/mission-control/src/lib/config.ts +67 -0
- package/mission-control/src/lib/db/index.ts +85 -1
- package/mission-control/src/lib/db/schema.ts +61 -3
- package/mission-control/src/lib/hooks.ts +309 -0
- package/mission-control/src/lib/memory/entities.ts +3 -2
- package/mission-control/src/lib/memory/extract.ts +2 -1
- package/mission-control/src/lib/memory/retrieval.ts +3 -2
- package/mission-control/src/lib/nats.ts +66 -1
- package/mission-control/src/lib/parsers/task-markdown.ts +52 -2
- package/mission-control/src/lib/parsers/transcript.ts +4 -4
- package/mission-control/src/lib/scheduler.ts +12 -11
- package/mission-control/src/lib/sync/mesh-kv.ts +279 -0
- package/mission-control/src/lib/sync/tasks.ts +23 -1
- package/mission-control/src/lib/task-id.ts +32 -0
- package/mission-control/src/lib/tts/index.ts +33 -9
- package/mission-control/src/middleware.ts +82 -0
- package/mission-control/tsconfig.json +2 -1
- package/mission-control/vitest.config.ts +14 -0
- package/package.json +15 -2
- package/services/launchd/ai.openclaw.log-rotate.plist +11 -0
- package/services/launchd/ai.openclaw.mesh-deploy-listener.plist +4 -0
- package/services/launchd/ai.openclaw.mesh-health-publisher.plist +4 -0
- package/services/launchd/ai.openclaw.mission-control.plist +1 -1
- package/services/service-manifest.json +1 -1
- package/skills/cc-godmode/references/agents.md +8 -8
- package/uninstall.sh +37 -9
- package/workspace-bin/memory-daemon.mjs +199 -5
- package/workspace-bin/session-search.mjs +204 -0
- package/workspace-bin/web-fetch.mjs +65 -0
package/bin/mesh-bridge.js
CHANGED
|
@@ -21,7 +21,7 @@ const path = require('path');
|
|
|
21
21
|
const { readTasks, updateTaskInPlace, isoTimestamp, ACTIVE_TASKS_PATH } = require('../lib/kanban-io');
|
|
22
22
|
|
|
23
23
|
const sc = StringCodec();
|
|
24
|
-
const { NATS_URL } = require('../lib/nats-resolve');
|
|
24
|
+
const { NATS_URL, natsConnectOpts } = require('../lib/nats-resolve');
|
|
25
25
|
const DISPATCH_INTERVAL = parseInt(process.env.BRIDGE_DISPATCH_INTERVAL || '10000'); // 10s
|
|
26
26
|
const LOG_DIR = path.join(process.env.HOME, '.openclaw', 'workspace', 'memory', 'mesh-logs');
|
|
27
27
|
const WORKSPACE = path.join(process.env.HOME, '.openclaw', 'workspace');
|
|
@@ -285,6 +285,55 @@ function handleCollabEvent(eventType, taskId, data) {
|
|
|
285
285
|
log(`COLLAB ABORTED: ${taskId} — ${session.result?.summary || 'unknown reason'}`);
|
|
286
286
|
break;
|
|
287
287
|
|
|
288
|
+
// Circling Strategy events
|
|
289
|
+
case 'collab.circling_step_started': {
|
|
290
|
+
if (!dispatched.has(taskId)) {
|
|
291
|
+
// Auto-track CLI-submitted circling tasks
|
|
292
|
+
try {
|
|
293
|
+
const tasks = readTasks(ACTIVE_TASKS_PATH);
|
|
294
|
+
if (tasks.find(t => t.task_id === taskId && t.execution === 'mesh')) {
|
|
295
|
+
dispatched.add(taskId);
|
|
296
|
+
lastHeartbeat.set(taskId, Date.now());
|
|
297
|
+
log(`CIRCLING AUTO-TRACK: ${taskId} (CLI-submitted)`);
|
|
298
|
+
} else { break; }
|
|
299
|
+
} catch { break; }
|
|
300
|
+
}
|
|
301
|
+
const c = session.circling || {};
|
|
302
|
+
const stepName = c.phase === 'init' ? 'Init'
|
|
303
|
+
: c.phase === 'finalization' ? 'Finalization'
|
|
304
|
+
: `SR${c.current_subround}/${c.max_subrounds} Step${c.current_step}`;
|
|
305
|
+
log(`CIRCLING ${stepName}: started for ${taskId}`);
|
|
306
|
+
updateTaskInPlace(ACTIVE_TASKS_PATH, taskId, {
|
|
307
|
+
circling_phase: c.phase || null,
|
|
308
|
+
circling_subround: c.current_subround || 0,
|
|
309
|
+
circling_step: c.current_step || 0,
|
|
310
|
+
next_action: `${stepName} in progress (${session.nodes?.length || '?'} nodes)`,
|
|
311
|
+
updated_at: isoTimestamp(),
|
|
312
|
+
});
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case 'collab.circling_gate': {
|
|
317
|
+
const cg = session.circling || {};
|
|
318
|
+
// Extract blocked reviewer summaries from the last round (if any)
|
|
319
|
+
const lastRound = session.rounds?.[session.rounds.length - 1];
|
|
320
|
+
const blockedVotes = lastRound?.reflections?.filter(r => r.vote === 'blocked') || [];
|
|
321
|
+
let gateMsg;
|
|
322
|
+
if (blockedVotes.length > 0) {
|
|
323
|
+
const reason = blockedVotes.map(r => r.summary).filter(Boolean).join('; ').slice(0, 150);
|
|
324
|
+
gateMsg = `[GATE] SR${cg.current_subround} blocked — ${reason || 'reviewer flagged concern'}`;
|
|
325
|
+
} else {
|
|
326
|
+
gateMsg = `[GATE] SR${cg.current_subround} complete — review reconciliationDoc and approve/reject`;
|
|
327
|
+
}
|
|
328
|
+
log(`CIRCLING GATE: ${taskId} — SR${cg.current_subround} waiting for approval`);
|
|
329
|
+
updateTaskInPlace(ACTIVE_TASKS_PATH, taskId, {
|
|
330
|
+
status: 'waiting-user',
|
|
331
|
+
next_action: gateMsg,
|
|
332
|
+
updated_at: isoTimestamp(),
|
|
333
|
+
});
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
|
|
288
337
|
default:
|
|
289
338
|
log(`COLLAB EVENT: ${eventType} for ${taskId}`);
|
|
290
339
|
}
|
|
@@ -677,8 +726,9 @@ async function main() {
|
|
|
677
726
|
log(` Dispatch interval: ${DISPATCH_INTERVAL / 1000}s`);
|
|
678
727
|
log(` Mode: ${DRY_RUN ? 'dry run' : 'live'}`);
|
|
679
728
|
|
|
729
|
+
const natsOpts = natsConnectOpts();
|
|
680
730
|
nc = await connect({
|
|
681
|
-
|
|
731
|
+
...natsOpts,
|
|
682
732
|
timeout: 5000,
|
|
683
733
|
reconnect: true,
|
|
684
734
|
maxReconnectAttempts: 10,
|
|
@@ -730,6 +780,16 @@ async function main() {
|
|
|
730
780
|
const stalenessTimer = setInterval(checkStaleness, HEARTBEAT_CHECK_INTERVAL);
|
|
731
781
|
log(`Heartbeat staleness check: every ${HEARTBEAT_CHECK_INTERVAL / 1000}s (warn at ${STALE_WARNING_MS / 60000}m)`);
|
|
732
782
|
|
|
783
|
+
// Wake signal — MC publishes mesh.bridge.wake after creating a mesh task
|
|
784
|
+
// so the bridge picks it up in ~1s instead of waiting for the next poll cycle
|
|
785
|
+
let wakeResolve = null;
|
|
786
|
+
const wakeSub = nc.subscribe('mesh.bridge.wake', {
|
|
787
|
+
callback: () => {
|
|
788
|
+
log('WAKE: received wake signal, triggering immediate poll');
|
|
789
|
+
if (wakeResolve) { wakeResolve(); wakeResolve = null; }
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
|
|
733
793
|
// Dispatch loop (polls active-tasks.md)
|
|
734
794
|
while (running) {
|
|
735
795
|
try {
|
|
@@ -779,10 +839,16 @@ async function main() {
|
|
|
779
839
|
}
|
|
780
840
|
}
|
|
781
841
|
|
|
782
|
-
|
|
842
|
+
// Sleep until next poll OR wake signal, whichever comes first
|
|
843
|
+
await Promise.race([
|
|
844
|
+
new Promise(r => setTimeout(r, DISPATCH_INTERVAL)),
|
|
845
|
+
new Promise(r => { wakeResolve = r; }),
|
|
846
|
+
]);
|
|
847
|
+
wakeResolve = null;
|
|
783
848
|
}
|
|
784
849
|
|
|
785
850
|
clearInterval(stalenessTimer);
|
|
851
|
+
wakeSub.unsubscribe();
|
|
786
852
|
sub.unsubscribe();
|
|
787
853
|
await nc.drain();
|
|
788
854
|
log('Bridge stopped.');
|
|
@@ -36,6 +36,12 @@ const IS_MAC = os.platform() === "darwin";
|
|
|
36
36
|
|
|
37
37
|
const { ROLE_COMPONENTS } = require('../lib/mesh-roles');
|
|
38
38
|
|
|
39
|
+
// ── Circuit Breaker State ───────────────────────────────────────────────
|
|
40
|
+
let consecutiveFailures = 0;
|
|
41
|
+
let skipTicksRemaining = 0;
|
|
42
|
+
let lastErrorMsg = '';
|
|
43
|
+
let lastErrorRepeatCount = 0;
|
|
44
|
+
|
|
39
45
|
// ── Health Gathering ─────────────────────────────────────────────────────
|
|
40
46
|
// All the expensive execSync calls happen here, on our own schedule.
|
|
41
47
|
// No request timeout to race against.
|
|
@@ -226,11 +232,45 @@ async function main() {
|
|
|
226
232
|
|
|
227
233
|
// Publish immediately, then every interval
|
|
228
234
|
async function publish() {
|
|
235
|
+
// Circuit breaker: skip ticks during backoff
|
|
236
|
+
if (skipTicksRemaining > 0) {
|
|
237
|
+
skipTicksRemaining--;
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
229
241
|
try {
|
|
230
242
|
const health = gatherHealth();
|
|
231
243
|
await kv.put(NODE_ID, sc.encode(JSON.stringify(health)));
|
|
244
|
+
// Reset on success
|
|
245
|
+
if (consecutiveFailures > 0) {
|
|
246
|
+
console.log(`[health-publisher] recovered after ${consecutiveFailures} consecutive failures`);
|
|
247
|
+
}
|
|
248
|
+
consecutiveFailures = 0;
|
|
249
|
+
lastErrorMsg = '';
|
|
250
|
+
lastErrorRepeatCount = 0;
|
|
232
251
|
} catch (err) {
|
|
233
|
-
|
|
252
|
+
consecutiveFailures++;
|
|
253
|
+
const msg = err.message;
|
|
254
|
+
|
|
255
|
+
// Log dedup: after 3 identical consecutive errors, log every 10th
|
|
256
|
+
if (msg === lastErrorMsg) {
|
|
257
|
+
lastErrorRepeatCount++;
|
|
258
|
+
if (lastErrorRepeatCount === 3) {
|
|
259
|
+
console.error(`[health-publisher] suppressing repeated errors (${lastErrorRepeatCount} occurrences): ${msg}`);
|
|
260
|
+
} else if (lastErrorRepeatCount > 3 && lastErrorRepeatCount % 10 === 0) {
|
|
261
|
+
console.error(`[health-publisher] suppressing repeated errors (${lastErrorRepeatCount} occurrences): ${msg}`);
|
|
262
|
+
}
|
|
263
|
+
// Silently skip logs between dedup thresholds
|
|
264
|
+
} else {
|
|
265
|
+
lastErrorMsg = msg;
|
|
266
|
+
lastErrorRepeatCount = 1;
|
|
267
|
+
console.error("[health-publisher] publish failed:", msg);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Exponential backoff: skip 2^min(N,6) ticks (max ~64 ticks / ~16 min at 15s)
|
|
271
|
+
const backoffTicks = Math.pow(2, Math.min(consecutiveFailures, 6));
|
|
272
|
+
skipTicksRemaining = backoffTicks;
|
|
273
|
+
console.error(`[health-publisher] backoff: skipping next ${backoffTicks} ticks (failures=${consecutiveFailures})`);
|
|
234
274
|
}
|
|
235
275
|
}
|
|
236
276
|
|