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.
Files changed (134) hide show
  1. package/README.md +646 -3
  2. package/bin/hyperagent.mjs +419 -0
  3. package/bin/lane-watchdog.js +23 -2
  4. package/bin/mesh-agent.js +439 -28
  5. package/bin/mesh-bridge.js +69 -3
  6. package/bin/mesh-health-publisher.js +41 -1
  7. package/bin/mesh-task-daemon.js +821 -26
  8. package/bin/mesh.js +411 -20
  9. package/config/claude-settings.json +95 -0
  10. package/config/daemon.json.template +2 -1
  11. package/config/git-hooks/pre-commit +13 -0
  12. package/config/git-hooks/pre-push +12 -0
  13. package/config/harness-rules.json +174 -0
  14. package/config/plan-templates/team-bugfix.yaml +52 -0
  15. package/config/plan-templates/team-deploy.yaml +50 -0
  16. package/config/plan-templates/team-feature.yaml +71 -0
  17. package/config/roles/qa-engineer.yaml +36 -0
  18. package/config/roles/solidity-dev.yaml +51 -0
  19. package/config/roles/tech-architect.yaml +36 -0
  20. package/config/rules/framework/solidity.md +22 -0
  21. package/config/rules/framework/typescript.md +21 -0
  22. package/config/rules/framework/unity.md +21 -0
  23. package/config/rules/universal/design-docs.md +18 -0
  24. package/config/rules/universal/git-hygiene.md +18 -0
  25. package/config/rules/universal/security.md +19 -0
  26. package/config/rules/universal/test-standards.md +19 -0
  27. package/identity/DELEGATION.md +6 -6
  28. package/install.sh +296 -10
  29. package/lib/agent-activity.js +2 -2
  30. package/lib/circling-parser.js +119 -0
  31. package/lib/exec-safety.js +105 -0
  32. package/lib/hyperagent-store.mjs +652 -0
  33. package/lib/kanban-io.js +24 -31
  34. package/lib/llm-providers.js +16 -0
  35. package/lib/mcp-knowledge/bench.mjs +118 -0
  36. package/lib/mcp-knowledge/core.mjs +530 -0
  37. package/lib/mcp-knowledge/package.json +25 -0
  38. package/lib/mcp-knowledge/server.mjs +252 -0
  39. package/lib/mcp-knowledge/test.mjs +802 -0
  40. package/lib/memory-budget.mjs +261 -0
  41. package/lib/mesh-collab.js +483 -165
  42. package/lib/mesh-harness.js +427 -0
  43. package/lib/mesh-plans.js +79 -50
  44. package/lib/mesh-tasks.js +132 -49
  45. package/lib/nats-resolve.js +4 -4
  46. package/lib/plan-templates.js +226 -0
  47. package/lib/pre-compression-flush.mjs +322 -0
  48. package/lib/role-loader.js +292 -0
  49. package/lib/rule-loader.js +358 -0
  50. package/lib/session-store.mjs +461 -0
  51. package/lib/transcript-parser.mjs +292 -0
  52. package/mission-control/drizzle/soul_schema_update.sql +29 -0
  53. package/mission-control/drizzle.config.ts +1 -4
  54. package/mission-control/package-lock.json +1571 -83
  55. package/mission-control/package.json +6 -2
  56. package/mission-control/scripts/gen-chronology.js +3 -3
  57. package/mission-control/scripts/import-pipeline-v2.js +0 -16
  58. package/mission-control/scripts/import-pipeline.js +0 -15
  59. package/mission-control/src/app/api/cowork/clusters/[id]/members/route.ts +117 -0
  60. package/mission-control/src/app/api/cowork/clusters/[id]/route.ts +84 -0
  61. package/mission-control/src/app/api/cowork/clusters/route.ts +141 -0
  62. package/mission-control/src/app/api/cowork/dispatch/route.ts +128 -0
  63. package/mission-control/src/app/api/cowork/events/route.ts +65 -0
  64. package/mission-control/src/app/api/cowork/intervene/route.ts +259 -0
  65. package/mission-control/src/app/api/cowork/sessions/[id]/route.ts +37 -0
  66. package/mission-control/src/app/api/cowork/sessions/route.ts +64 -0
  67. package/mission-control/src/app/api/diagnostics/route.ts +97 -0
  68. package/mission-control/src/app/api/diagnostics/test-runner/route.ts +990 -0
  69. package/mission-control/src/app/api/memory/search/route.ts +6 -3
  70. package/mission-control/src/app/api/mesh/events/route.ts +95 -19
  71. package/mission-control/src/app/api/mesh/identity/route.ts +11 -0
  72. package/mission-control/src/app/api/mesh/tasks/[id]/route.ts +92 -0
  73. package/mission-control/src/app/api/mesh/tasks/route.ts +91 -0
  74. package/mission-control/src/app/api/souls/[id]/evolution/route.ts +21 -5
  75. package/mission-control/src/app/api/souls/[id]/prompt/route.ts +7 -1
  76. package/mission-control/src/app/api/souls/[id]/propagate/route.ts +14 -2
  77. package/mission-control/src/app/api/tasks/[id]/handoff/route.ts +8 -2
  78. package/mission-control/src/app/api/tasks/[id]/route.ts +90 -4
  79. package/mission-control/src/app/api/tasks/route.ts +21 -30
  80. package/mission-control/src/app/api/workspace/read/route.ts +11 -0
  81. package/mission-control/src/app/cowork/page.tsx +261 -0
  82. package/mission-control/src/app/diagnostics/page.tsx +385 -0
  83. package/mission-control/src/app/graph/page.tsx +26 -0
  84. package/mission-control/src/app/memory/page.tsx +1 -1
  85. package/mission-control/src/app/obsidian/page.tsx +36 -6
  86. package/mission-control/src/app/roadmap/page.tsx +24 -0
  87. package/mission-control/src/app/souls/page.tsx +2 -2
  88. package/mission-control/src/components/board/execution-config.tsx +431 -0
  89. package/mission-control/src/components/board/kanban-board.tsx +75 -9
  90. package/mission-control/src/components/board/kanban-column.tsx +135 -19
  91. package/mission-control/src/components/board/task-card.tsx +55 -2
  92. package/mission-control/src/components/board/unified-task-dialog.tsx +82 -4
  93. package/mission-control/src/components/cowork/cluster-card.tsx +176 -0
  94. package/mission-control/src/components/cowork/create-cluster-dialog.tsx +251 -0
  95. package/mission-control/src/components/cowork/dispatch-form.tsx +423 -0
  96. package/mission-control/src/components/cowork/role-picker.tsx +102 -0
  97. package/mission-control/src/components/cowork/session-card.tsx +284 -0
  98. package/mission-control/src/components/layout/sidebar.tsx +39 -2
  99. package/mission-control/src/lib/__tests__/daily-log.test.ts +82 -0
  100. package/mission-control/src/lib/__tests__/memory-md.test.ts +87 -0
  101. package/mission-control/src/lib/__tests__/mesh-kv-sync.test.ts +465 -0
  102. package/mission-control/src/lib/__tests__/mocks/mock-kv.ts +131 -0
  103. package/mission-control/src/lib/__tests__/status-kanban.test.ts +46 -0
  104. package/mission-control/src/lib/__tests__/task-markdown.test.ts +188 -0
  105. package/mission-control/src/lib/__tests__/wikilinks.test.ts +175 -0
  106. package/mission-control/src/lib/config.ts +67 -0
  107. package/mission-control/src/lib/db/index.ts +85 -1
  108. package/mission-control/src/lib/db/schema.ts +61 -3
  109. package/mission-control/src/lib/hooks.ts +309 -0
  110. package/mission-control/src/lib/memory/entities.ts +3 -2
  111. package/mission-control/src/lib/memory/extract.ts +2 -1
  112. package/mission-control/src/lib/memory/retrieval.ts +3 -2
  113. package/mission-control/src/lib/nats.ts +66 -1
  114. package/mission-control/src/lib/parsers/task-markdown.ts +52 -2
  115. package/mission-control/src/lib/parsers/transcript.ts +4 -4
  116. package/mission-control/src/lib/scheduler.ts +12 -11
  117. package/mission-control/src/lib/sync/mesh-kv.ts +279 -0
  118. package/mission-control/src/lib/sync/tasks.ts +23 -1
  119. package/mission-control/src/lib/task-id.ts +32 -0
  120. package/mission-control/src/lib/tts/index.ts +33 -9
  121. package/mission-control/src/middleware.ts +82 -0
  122. package/mission-control/tsconfig.json +2 -1
  123. package/mission-control/vitest.config.ts +14 -0
  124. package/package.json +15 -2
  125. package/services/launchd/ai.openclaw.log-rotate.plist +11 -0
  126. package/services/launchd/ai.openclaw.mesh-deploy-listener.plist +4 -0
  127. package/services/launchd/ai.openclaw.mesh-health-publisher.plist +4 -0
  128. package/services/launchd/ai.openclaw.mission-control.plist +1 -1
  129. package/services/service-manifest.json +1 -1
  130. package/skills/cc-godmode/references/agents.md +8 -8
  131. package/uninstall.sh +37 -9
  132. package/workspace-bin/memory-daemon.mjs +199 -5
  133. package/workspace-bin/session-search.mjs +204 -0
  134. package/workspace-bin/web-fetch.mjs +65 -0
@@ -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
- servers: NATS_URL,
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
- await new Promise(r => setTimeout(r, DISPATCH_INTERVAL));
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
- console.error("[health-publisher] publish failed:", err.message);
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