nexus-prime 7.9.22 → 7.9.23

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 (54) hide show
  1. package/dist/agents/adapters/ide-compat.d.ts +3 -3
  2. package/dist/agents/adapters/ide-compat.js +51 -1
  3. package/dist/agents/adapters/mcp/definitions.js +4 -1
  4. package/dist/agents/adapters/mcp/dispatch.js +66 -4
  5. package/dist/agents/adapters/mcp/handlers/orchestration.js +91 -0
  6. package/dist/agents/adapters/mcp/runHandler.js +3 -0
  7. package/dist/agents/adapters/mcp/util/detect-caller.js +21 -0
  8. package/dist/agents/adapters/mcp.js +1 -1
  9. package/dist/agents/adapters.d.ts +10 -1
  10. package/dist/agents/adapters.js +21 -0
  11. package/dist/agents/core/types.d.ts +1 -1
  12. package/dist/cli/hook.d.ts +4 -6
  13. package/dist/cli/hook.js +6 -8
  14. package/dist/cli/install-wizard.js +5 -1
  15. package/dist/cli.js +181 -15
  16. package/dist/core/types.d.ts +1 -1
  17. package/dist/dashboard/app/styles/board.css +85 -1
  18. package/dist/dashboard/app/views/board.js +56 -0
  19. package/dist/dashboard/app/views/memory.js +71 -10
  20. package/dist/dashboard/routes/events.js +3 -0
  21. package/dist/dashboard/selectors/operate-selector.js +5 -0
  22. package/dist/dashboard/selectors/runs-selector.js +5 -0
  23. package/dist/dashboard/server.js +6 -0
  24. package/dist/dashboard/types.d.ts +4 -0
  25. package/dist/engines/client-bootstrap.d.ts +5 -1
  26. package/dist/engines/client-bootstrap.js +105 -10
  27. package/dist/engines/client-registry.js +51 -0
  28. package/dist/engines/event-bus.d.ts +16 -2
  29. package/dist/engines/feature-registry.js +1 -0
  30. package/dist/engines/instruction-gateway.d.ts +9 -0
  31. package/dist/engines/instruction-gateway.js +113 -4
  32. package/dist/engines/memory/types.d.ts +28 -0
  33. package/dist/engines/memory-bridge.d.ts +1 -1
  34. package/dist/engines/memory-bridge.js +1 -1
  35. package/dist/engines/memory.d.ts +5 -0
  36. package/dist/engines/memory.js +144 -12
  37. package/dist/engines/orchestrator/decision-spine.d.ts +26 -0
  38. package/dist/engines/orchestrator/decision-spine.js +145 -6
  39. package/dist/engines/orchestrator/funnel.js +8 -1
  40. package/dist/engines/orchestrator/scoring.d.ts +1 -1
  41. package/dist/engines/orchestrator/scoring.js +24 -2
  42. package/dist/engines/orchestrator.d.ts +3 -0
  43. package/dist/engines/orchestrator.js +73 -13
  44. package/dist/engines/peer-connectors.d.ts +1 -1
  45. package/dist/engines/peer-connectors.js +9 -2
  46. package/dist/engines/runtime-registry.d.ts +9 -0
  47. package/dist/index.js +9 -0
  48. package/dist/install/state-locator.d.ts +1 -1
  49. package/dist/install/state-locator.js +3 -0
  50. package/dist/synapse/bootstrap.js +3 -0
  51. package/dist/synapse/mandate/pipeline.js +52 -5
  52. package/dist/synapse/sorties/runner.js +32 -0
  53. package/dist/synapse/types.d.ts +27 -0
  54. package/package.json +1 -1
@@ -2,10 +2,10 @@
2
2
  * Nexus Prime — IDE Compatibility Utilities
3
3
  * Provides MCP config templates and detection logic for all supported IDEs.
4
4
  *
5
- * Supported: VS Code (Claude Code ext), JetBrains, Cursor, Windsurf, Zed, Continue.dev, Aider, Cline, Codex
5
+ * Supported: VS Code (Claude Code ext), JetBrains, Cursor, Windsurf, Zed, Continue.dev, Aider, Cline, Codex, and peer agent clients.
6
6
  */
7
- export type IDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'cline' | 'zed' | 'codex';
8
- export type CallerIDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'zed' | 'codex';
7
+ export type IDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'cline' | 'openclaw' | 'hermes' | 'nanoclaw' | 'picoclaw' | 'zed' | 'codex';
8
+ export type CallerIDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'openclaw' | 'hermes' | 'nanoclaw' | 'picoclaw' | 'zed' | 'codex';
9
9
  export interface McpConfigOutput {
10
10
  /** Absolute path where the config should be written */
11
11
  configPath: string | null;
@@ -2,7 +2,7 @@
2
2
  * Nexus Prime — IDE Compatibility Utilities
3
3
  * Provides MCP config templates and detection logic for all supported IDEs.
4
4
  *
5
- * Supported: VS Code (Claude Code ext), JetBrains, Cursor, Windsurf, Zed, Continue.dev, Aider, Cline, Codex
5
+ * Supported: VS Code (Claude Code ext), JetBrains, Cursor, Windsurf, Zed, Continue.dev, Aider, Cline, Codex, and peer agent clients.
6
6
  */
7
7
  import { existsSync } from 'fs';
8
8
  import { homedir } from 'os';
@@ -29,6 +29,18 @@ function detectCallerIDEFromEnv() {
29
29
  if (hasAnyEnvPrefix('WINDSURF_')) {
30
30
  return 'windsurf';
31
31
  }
32
+ if (hasAnyEnvPrefix('OPENCLAW_') || hasAnyEnvPrefix('ANTIGRAVITY_')) {
33
+ return 'openclaw';
34
+ }
35
+ if (hasAnyEnvPrefix('HERMES_')) {
36
+ return 'hermes';
37
+ }
38
+ if (hasAnyEnvPrefix('NANOCLAW_')) {
39
+ return 'nanoclaw';
40
+ }
41
+ if (hasAnyEnvPrefix('PICOCLAW_')) {
42
+ return 'picoclaw';
43
+ }
32
44
  if (hasAnyEnvPrefix('CONTINUE_')) {
33
45
  return 'continue';
34
46
  }
@@ -46,6 +58,14 @@ function detectCallerIDEFromFilesystem(startDir) {
46
58
  return 'continue';
47
59
  if (existsSync(join(current, '.windsurf')))
48
60
  return 'windsurf';
61
+ if (existsSync(join(current, '.openclaw')))
62
+ return 'openclaw';
63
+ if (existsSync(join(current, '.hermes')))
64
+ return 'hermes';
65
+ if (existsSync(join(current, '.nanoclaw')))
66
+ return 'nanoclaw';
67
+ if (existsSync(join(current, '.picoclaw')))
68
+ return 'picoclaw';
49
69
  if (existsSync(join(current, '.zed')))
50
70
  return 'zed';
51
71
  const parent = dirname(current);
@@ -84,6 +104,22 @@ export function detectInstalledIDEs(workspaceRoot) {
84
104
  existsSync(join(home, '.windsurf'))) {
85
105
  detected.push('windsurf');
86
106
  }
107
+ if (existsSync(join(workspaceRoot, '.openclaw')) ||
108
+ existsSync(join(home, '.openclaw'))) {
109
+ detected.push('openclaw');
110
+ }
111
+ if (existsSync(join(workspaceRoot, '.hermes')) ||
112
+ existsSync(join(home, '.hermes'))) {
113
+ detected.push('hermes');
114
+ }
115
+ if (existsSync(join(workspaceRoot, '.nanoclaw')) ||
116
+ existsSync(join(home, '.nanoclaw'))) {
117
+ detected.push('nanoclaw');
118
+ }
119
+ if (existsSync(join(workspaceRoot, '.picoclaw')) ||
120
+ existsSync(join(home, '.picoclaw'))) {
121
+ detected.push('picoclaw');
122
+ }
87
123
  // Continue.dev: .continue/ directory
88
124
  if (existsSync(join(workspaceRoot, '.continue')) ||
89
125
  existsSync(join(home, '.continue'))) {
@@ -140,6 +176,20 @@ export function getMcpConfigForIDE(ide, workspaceRoot) {
140
176
  }, null, 2);
141
177
  return { configPath, content, merge: true };
142
178
  }
179
+ case 'openclaw':
180
+ case 'hermes':
181
+ case 'nanoclaw':
182
+ case 'picoclaw': {
183
+ const configPath = ide === 'openclaw'
184
+ ? join(home, '.openclaw', 'openclaw.json')
185
+ : join(home, `.${ide}`, 'mcp.json');
186
+ const content = JSON.stringify({
187
+ mcpServers: {
188
+ 'nexus-prime': entry,
189
+ },
190
+ }, null, 2);
191
+ return { configPath, content, merge: true };
192
+ }
143
193
  case 'continue': {
144
194
  // Continue.dev uses ~/.continue/config.json
145
195
  const configPath = join(home, '.continue', 'config.json');
@@ -279,7 +279,10 @@ export function buildMcpToolDefinitions() {
279
279
  crews: { type: 'array', items: { type: 'string' }, description: 'Optional hard crew selectors' },
280
280
  specialists: { type: 'array', items: { type: 'string' }, description: 'Optional hard specialist selectors' },
281
281
  optimizationProfile: { type: 'string', enum: ['standard', 'max'], description: 'Planner optimization profile override' },
282
- executionPreset: { type: 'string', enum: ['fast', 'balanced', 'deep', 'release'], description: 'Optional execution preset that maps orchestration depth, verification strictness, and backend routing' }
282
+ executionPreset: { type: 'string', enum: ['fast', 'balanced', 'deep', 'release'], description: 'Optional execution preset that maps orchestration depth, verification strictness, and backend routing' },
283
+ background: { type: 'boolean', description: 'When true, return a queued receipt immediately and let the run continue in the async gate.' },
284
+ async: { type: 'boolean', description: 'Alias for background; useful for clients that prefer explicit async orchestration.' },
285
+ waitMs: { type: 'number', description: 'Optional bounded wait before returning a queued receipt. Clamped to 45 seconds; normal orchestrate calls default to 15 seconds.' }
283
286
  },
284
287
  required: ['prompt'],
285
288
  },
@@ -16,9 +16,18 @@ import { handleMiscGroup } from './handlers/misc.js';
16
16
  import { handleWorkforceGroup } from './handlers/workforce.js';
17
17
  import { nexusEventBus } from '../../../engines/event-bus.js';
18
18
  import { recordToolInvocation } from './tool-health.js';
19
- import { withAsyncGate } from './async-gate.js';
19
+ import { getAsyncGate, withAsyncGate } from './async-gate.js';
20
20
  import { getCircuitManager, checkBackpressure } from './circuit.js';
21
- import { createRun } from '../../../engines/orchestrator/store.js';
21
+ import { completeRun, createRun, failRun, updateStage } from '../../../engines/orchestrator/store.js';
22
+ function summarizeAsyncMcpResult(toolName, result) {
23
+ const text = result?.content
24
+ ?.map((part) => part?.type === 'text' ? String(part.text ?? '') : '')
25
+ .filter(Boolean)
26
+ .join('\n')
27
+ .trim();
28
+ const firstLine = text?.split('\n').find((line) => line.trim())?.trim();
29
+ return (firstLine ? `${toolName}: ${firstLine}` : `${toolName} completed asynchronously`).slice(0, 1024);
30
+ }
22
31
  /** Tools that may exceed the MCP client timeout (~60s). Wrap in async gate. */
23
32
  const SLOW_TOOLS = new Set([
24
33
  'nexus_orchestrate',
@@ -69,6 +78,37 @@ const TOOL_ETA_MS = {
69
78
  nexus_autofix: 45_000,
70
79
  nexus_hypertune_max: 45_000,
71
80
  };
81
+ const DEFAULT_MAX_SYNC_MS = 2000;
82
+ const ORCHESTRATE_DEFAULT_MAX_SYNC_MS = 15_000;
83
+ const MAX_CLIENT_SYNC_WAIT_MS = 45_000;
84
+ function isTruthyFlag(value) {
85
+ if (value === true)
86
+ return true;
87
+ if (typeof value === 'number')
88
+ return value > 0;
89
+ if (typeof value === 'string')
90
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
91
+ return false;
92
+ }
93
+ function coerceBoundedWaitMs(value) {
94
+ const numeric = Number(value);
95
+ if (!Number.isFinite(numeric) || numeric < 0)
96
+ return undefined;
97
+ return Math.min(MAX_CLIENT_SYNC_WAIT_MS, Math.floor(numeric));
98
+ }
99
+ function shouldReturnQueuedReceipt(toolName, args) {
100
+ if (!SLOW_TOOLS.has(toolName))
101
+ return false;
102
+ return isTruthyFlag(args.background)
103
+ || isTruthyFlag(args.async)
104
+ || isTruthyFlag(args.queue)
105
+ || isTruthyFlag(args.detach);
106
+ }
107
+ function resolveMaxSyncMs(toolName, args) {
108
+ return coerceBoundedWaitMs(args.waitMs)
109
+ ?? coerceBoundedWaitMs(args.maxSyncMs)
110
+ ?? (toolName === 'nexus_orchestrate' ? ORCHESTRATE_DEFAULT_MAX_SYNC_MS : DEFAULT_MAX_SYNC_MS);
111
+ }
72
112
  /**
73
113
  * Route a tool call to the appropriate handler group.
74
114
  *
@@ -127,9 +167,9 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
127
167
  }, {
128
168
  tool: toolName,
129
169
  args,
130
- maxSyncMs: 2000,
170
+ maxSyncMs: resolveMaxSyncMs(toolName, args),
131
171
  etaMs: TOOL_ETA_MS[toolName],
132
- alwaysQueue: toolName === 'nexus_orchestrate',
172
+ alwaysQueue: shouldReturnQueuedReceipt(toolName, args),
133
173
  });
134
174
  if ('queued' in gated && gated.queued) {
135
175
  // Persist orchestration run record for durable stage tracking
@@ -138,6 +178,7 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
138
178
  const client = hctx.getToolProfile();
139
179
  try {
140
180
  createRun(gated.runId, goal, client);
181
+ updateStage(gated.runId, 'executing', 20);
141
182
  }
142
183
  catch { /* non-fatal */ }
143
184
  }
@@ -147,8 +188,24 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
147
188
  const runId = gated.runId;
148
189
  let unsubOk = null;
149
190
  let unsubFail = null;
191
+ let finalized = false;
150
192
  const finalize = (success) => {
193
+ if (finalized)
194
+ return;
195
+ finalized = true;
151
196
  const durationMs = Date.now() - startedAt;
197
+ if (toolName === 'nexus_orchestrate') {
198
+ try {
199
+ const job = getAsyncGate().getJob(runId);
200
+ if (success) {
201
+ completeRun(runId, summarizeAsyncMcpResult(toolName, job?.result));
202
+ }
203
+ else {
204
+ failRun(runId, job?.error ?? `${toolName} failed asynchronously`);
205
+ }
206
+ }
207
+ catch { /* durable run updates are best-effort */ }
208
+ }
152
209
  try {
153
210
  circuit.recordComplete(toolName, durationMs, success);
154
211
  }
@@ -166,6 +223,11 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
166
223
  if (ev.runId === runId)
167
224
  finalize(false);
168
225
  });
226
+ const existingJob = getAsyncGate().getJob(runId);
227
+ if (existingJob?.status === 'completed')
228
+ finalize(true);
229
+ if (existingJob?.status === 'failed')
230
+ finalize(false);
169
231
  // Return async receipt as MCP text so agent can poll
170
232
  rawResult = {
171
233
  content: [{
@@ -55,6 +55,82 @@ export function extractSkillSelectorsFromPrompt(prompt) {
55
55
  export function inferSpawnWorkersIntent(actions) {
56
56
  return actions.length > 0 ? 'mutate' : 'plan';
57
57
  }
58
+ function namedValue(value) {
59
+ if (typeof value === 'string')
60
+ return value.trim();
61
+ if (!value || typeof value !== 'object')
62
+ return '';
63
+ const record = value;
64
+ return String(record.name ?? record.id ?? record.specialistId ?? record.skillId ?? record.workflowId ?? record.crewId ?? record.workerId ?? '').trim();
65
+ }
66
+ function asStringList(values, limit = 8) {
67
+ const array = Array.isArray(values) ? values : values ? [values] : [];
68
+ return [...new Set(array.map(namedValue).filter(Boolean))].slice(0, limit);
69
+ }
70
+ function formatSelectionList(values, fallback = 'none') {
71
+ return values.length > 0 ? values.join(', ') : fallback;
72
+ }
73
+ function formatModelRoute(route) {
74
+ if (!route)
75
+ return 'default runtime route';
76
+ const worker = String(route.workerTier ?? 'T1');
77
+ const reviewer = route.reviewerTier ? ` + reviewer ${route.reviewerTier}` : '';
78
+ const reason = route.reason ? ` - ${route.reason}` : '';
79
+ return `${worker}${reviewer}${reason}`;
80
+ }
81
+ function formatBudgetRoute(plan) {
82
+ const budgets = plan?.budgets ?? {};
83
+ const policy = plan?.executionPolicy?.agentFlow ?? {};
84
+ const total = Number(budgets.totalTokens ?? 0);
85
+ const codeBlocks = Number(budgets.codeBlocks ?? budgets.codeBlockPolicy?.reservedTokens ?? 0);
86
+ const cap = Number(policy.maxTaskCostUsd ?? 0);
87
+ const parts = [
88
+ total > 0 ? `${total.toLocaleString()} tokens` : 'budget pending',
89
+ codeBlocks > 0 ? `${codeBlocks.toLocaleString()} code-block tokens` : null,
90
+ cap > 0 ? `$${cap.toFixed(2)} task cap` : null,
91
+ ].filter(Boolean);
92
+ return parts.join(' · ');
93
+ }
94
+ function buildSelectionSummary(execution, runtimeUsage) {
95
+ const selectionPlan = execution.selectionPlan ?? {};
96
+ const selected = selectionPlan.selected ?? {};
97
+ const planner = execution.plannerState ?? {};
98
+ const taskGraph = runtimeUsage.taskGraph ?? execution.taskGraph ?? {};
99
+ const workerPlan = runtimeUsage.workerPlan ?? execution.workerPlan ?? {};
100
+ const audit = runtimeUsage.artifactSelectionAudit ?? execution.artifactSelectionAudit ?? {};
101
+ const crew = asStringList(selected.crews, 2)[0]
102
+ ?? namedValue(planner.selectedCrew)
103
+ ?? 'baseline path';
104
+ const specialists = asStringList(selected.specialists, 8).length > 0
105
+ ? asStringList(selected.specialists, 8)
106
+ : asStringList(planner.selectedSpecialists, 8);
107
+ const skills = asStringList(selected.skills, 8).length > 0
108
+ ? asStringList(selected.skills, 8)
109
+ : asStringList(execution.activeSkills, 8).concat(asStringList(planner.selectedSkills, 8)).slice(0, 8);
110
+ const workflows = asStringList(selected.workflows, 8).length > 0
111
+ ? asStringList(selected.workflows, 8)
112
+ : asStringList(execution.activeWorkflows, 8).concat(asStringList(planner.selectedWorkflows, 8)).slice(0, 8);
113
+ const workers = asStringList(selected.workers, 8).length > 0
114
+ ? asStringList(selected.workers, 8)
115
+ : asStringList(execution.workerManifests, 8);
116
+ return {
117
+ crew,
118
+ specialists,
119
+ skills: [...new Set(skills)],
120
+ workflows: [...new Set(workflows)],
121
+ hooks: asStringList(selected.hooks, 8),
122
+ automations: asStringList(selected.automations, 8),
123
+ workers,
124
+ modelRoute: selectionPlan.modelRoute ?? execution.requestBrief?.modelPolicy,
125
+ budgetRoute: formatBudgetRoute(selectionPlan),
126
+ agentFlow: selectionPlan.executionPolicy?.agentFlow,
127
+ phaseCount: Array.isArray(taskGraph.phases) ? taskGraph.phases.length : 0,
128
+ workerLaneCount: Number(workerPlan.totalWorkers ?? workers.length ?? execution.workerResults?.length ?? 0),
129
+ mode: String(workerPlan.mode ?? execution.mode ?? 'autonomous'),
130
+ auditSelected: Array.isArray(audit.selected) ? audit.selected.length : 0,
131
+ auditRejected: Array.isArray(audit.rejected) ? audit.rejected.length : 0,
132
+ };
133
+ }
58
134
  export async function handleOrchestrationGroup(toolName, hctx, request, args, ctx) {
59
135
  const runtimeError = requireRuntime(hctx);
60
136
  if (runtimeError)
@@ -457,6 +533,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
457
533
  result.modifiedFiles.forEach((file) => hctx.sessionDNA.recordFileModified(file));
458
534
  });
459
535
  const runtimeUsage = hctx.getRuntime().getUsageSnapshot();
536
+ const selectionSummary = buildSelectionSummary(execution, runtimeUsage);
460
537
  hctx.sessionDNA.recordDecision('Orchestrated autonomous run completed', execution.result || summarizeExecution(execution), execution.state === 'merged' ? 0.95
461
538
  : execution.state === 'inspected' ? 0.85
462
539
  : execution.state === 'rolled_back' ? 0.55
@@ -491,6 +568,8 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
491
568
  worktreeHealth: runtimeUsage.worktreeHealth,
492
569
  ragUsageSummary: runtimeUsage.ragUsageSummary ?? execution.ragUsageSummary,
493
570
  memoryScopeUsage: runtimeUsage.memoryScopeUsage ?? execution.memoryScopeUsage,
571
+ selection: selectionSummary,
572
+ modelRoute: selectionSummary.modelRoute,
494
573
  verifiedWorkers,
495
574
  continuationChildren: execution.continuationChildren.length,
496
575
  executionPreset: preset ? { id: preset.id, name: preset.name, summary: preset.summary } : undefined,
@@ -525,6 +604,10 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
525
604
  ragUsageSummary: runtimeUsage.ragUsageSummary ?? execution.ragUsageSummary,
526
605
  memoryScopeUsage: runtimeUsage.memoryScopeUsage ?? execution.memoryScopeUsage,
527
606
  memoryReconciliationSummary: runtimeUsage.memoryReconciliationSummary ?? execution.memoryReconciliationSummary,
607
+ requestBrief: execution.requestBrief,
608
+ selectionPlan: execution.selectionPlan,
609
+ selection: selectionSummary,
610
+ modelRoute: selectionSummary.modelRoute,
528
611
  tokens: execution.tokenTelemetry,
529
612
  verifiedWorkers,
530
613
  continuationChildren: execution.continuationChildren,
@@ -545,6 +628,14 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
545
628
  `Run ID: ${execution.runId}`,
546
629
  `Summary: ${summarizeExecution(execution)}`,
547
630
  `Crew: ${execution.plannerState?.selectedCrew?.name || 'baseline path'}`,
631
+ `Decomposition: ${selectionSummary.phaseCount} phase(s), ${selectionSummary.workerLaneCount} worker lane(s), ${selectionSummary.mode}`,
632
+ `Hired/selected: crew ${selectionSummary.crew}; specialists ${formatSelectionList(selectionSummary.specialists)}; workflows ${formatSelectionList(selectionSummary.workflows)}; skills ${formatSelectionList(selectionSummary.skills)}`,
633
+ `Model route: ${formatModelRoute(selectionSummary.modelRoute)}`,
634
+ `Budget route: ${selectionSummary.budgetRoute || 'budget pending'}`,
635
+ selectionSummary.agentFlow?.stages?.length
636
+ ? `AgentFlow gates: ${selectionSummary.agentFlow.stages.map((stage) => `${stage.stage}:${stage.ownerRole}`).join(' -> ')}`
637
+ : null,
638
+ `Selection audit: ${selectionSummary.auditSelected} selected, ${selectionSummary.auditRejected} rejected`,
548
639
  `Verification: ${verifiedWorkers}/${execution.workerResults.length} worker(s) verified`,
549
640
  `Tokens: saved ${Number(execution.tokenTelemetry?.savedTokens || 0).toLocaleString()} · compression ${Number(execution.tokenTelemetry?.compressionPct || 0)}%`,
550
641
  autoTokenApplyNote || null,
@@ -21,6 +21,9 @@ const CLIENT_TIMEOUTS = {
21
21
  'claude-code': 60_000,
22
22
  'cursor': 30_000,
23
23
  'openclaw': 90_000,
24
+ 'hermes': 90_000,
25
+ 'nanoclaw': 90_000,
26
+ 'picoclaw': 90_000,
24
27
  'opencode': 90_000,
25
28
  'windsurf': 90_000,
26
29
  'antigravity': 90_000,
@@ -17,6 +17,9 @@ const ENV_MAP = [
17
17
  [['CURSOR_HOME', 'CURSOR_SESSION'], 'cursor'],
18
18
  [['OPENCODE_HOME'], 'opencode'],
19
19
  [['WINDSURF_HOME', 'WINDSURF_SESSION'], 'windsurf'],
20
+ [['HERMES_HOME', 'HERMES_SESSION'], 'hermes'],
21
+ [['NANOCLAW_HOME', 'NANOCLAW_SESSION'], 'nanoclaw'],
22
+ [['PICOCLAW_HOME', 'PICOCLAW_SESSION'], 'picoclaw'],
20
23
  ];
21
24
  // Module-level store for the caller name obtained from the MCP initialize
22
25
  // handshake. Takes precedence over env-var and ps fallback once set.
@@ -52,6 +55,18 @@ export function setMcpClientName(rawName) {
52
55
  _mcpHandshakeName = 'windsurf';
53
56
  return;
54
57
  }
58
+ if (lower.includes('hermes')) {
59
+ _mcpHandshakeName = 'hermes';
60
+ return;
61
+ }
62
+ if (lower.includes('nanoclaw') || lower.includes('nano-claw')) {
63
+ _mcpHandshakeName = 'nanoclaw';
64
+ return;
65
+ }
66
+ if (lower.includes('picoclaw') || lower.includes('pico-claw')) {
67
+ _mcpHandshakeName = 'picoclaw';
68
+ return;
69
+ }
55
70
  if (lower.includes('anticlaw') || lower.includes('openclaw')) {
56
71
  _mcpHandshakeName = 'openclaw';
57
72
  return;
@@ -95,6 +110,12 @@ export async function detectCallerAsync() {
95
110
  return 'opencode';
96
111
  if (ps.includes('windsurf'))
97
112
  return 'windsurf';
113
+ if (ps.includes('hermes'))
114
+ return 'hermes';
115
+ if (ps.includes('nanoclaw') || ps.includes('nano-claw'))
116
+ return 'nanoclaw';
117
+ if (ps.includes('picoclaw') || ps.includes('pico-claw'))
118
+ return 'picoclaw';
98
119
  if (ps.includes('antigravity') || ps.includes('openclaw'))
99
120
  return 'openclaw';
100
121
  }
@@ -719,7 +719,7 @@ export class MCPAdapter {
719
719
  try {
720
720
  this.getRuntime().recordClientInstructionStatus({
721
721
  clientId: this.name,
722
- clientFamily: this.name === 'openclaw' ? 'antigravity' : this.name,
722
+ clientFamily: this.name === 'openclaw' || this.name === 'antigravity' ? 'antigravity' : this.name,
723
723
  toolProfile: profile,
724
724
  status: profile === 'autonomous' ? 'guided' : 'manual',
725
725
  summary: this.describeClientInstructionStatus(profile),
@@ -24,6 +24,15 @@ declare abstract class RenderedInstructionAdapter implements Adapter {
24
24
  export declare class OpenClawAdapter extends RenderedInstructionAdapter {
25
25
  constructor();
26
26
  }
27
+ export declare class HermesAdapter extends RenderedInstructionAdapter {
28
+ constructor();
29
+ }
30
+ export declare class NanoClawAdapter extends RenderedInstructionAdapter {
31
+ constructor();
32
+ }
33
+ export declare class PicoClawAdapter extends RenderedInstructionAdapter {
34
+ constructor();
35
+ }
27
36
  export declare class ClaudeCodeAdapter extends RenderedInstructionAdapter {
28
37
  constructor();
29
38
  }
@@ -60,5 +69,5 @@ export declare class CustomAdapter extends RenderedInstructionAdapter {
60
69
  setSendHandler(handler: (message: NetworkMessage) => Promise<void>): void;
61
70
  setReceiveHandler(handler: (message: NetworkMessage) => void): void;
62
71
  }
63
- export type AdapterType = 'openclaw' | 'claude-code' | 'ruflo' | 'langchain' | 'autogen' | 'custom' | 'mcp' | 'codex' | 'opencode' | 'cursor' | 'windsurf' | 'aider' | 'continue' | 'cline';
72
+ export type AdapterType = 'openclaw' | 'hermes' | 'nanoclaw' | 'picoclaw' | 'claude-code' | 'ruflo' | 'langchain' | 'autogen' | 'custom' | 'mcp' | 'codex' | 'opencode' | 'cursor' | 'windsurf' | 'aider' | 'continue' | 'cline';
64
73
  export declare function createAdapter(type: AdapterType, customName?: string): Adapter;
@@ -78,6 +78,21 @@ export class OpenClawAdapter extends RenderedInstructionAdapter {
78
78
  super('openclaw', 'openclaw');
79
79
  }
80
80
  }
81
+ export class HermesAdapter extends RenderedInstructionAdapter {
82
+ constructor() {
83
+ super('hermes', 'hermes');
84
+ }
85
+ }
86
+ export class NanoClawAdapter extends RenderedInstructionAdapter {
87
+ constructor() {
88
+ super('nanoclaw', 'nanoclaw');
89
+ }
90
+ }
91
+ export class PicoClawAdapter extends RenderedInstructionAdapter {
92
+ constructor() {
93
+ super('picoclaw', 'picoclaw');
94
+ }
95
+ }
81
96
  export class ClaudeCodeAdapter extends RenderedInstructionAdapter {
82
97
  constructor() {
83
98
  super('claude-code', 'claude-code');
@@ -158,6 +173,12 @@ export function createAdapter(type, customName) {
158
173
  switch (type) {
159
174
  case 'openclaw':
160
175
  return new OpenClawAdapter();
176
+ case 'hermes':
177
+ return new HermesAdapter();
178
+ case 'nanoclaw':
179
+ return new NanoClawAdapter();
180
+ case 'picoclaw':
181
+ return new PicoClawAdapter();
161
182
  case 'claude-code':
162
183
  return new ClaudeCodeAdapter();
163
184
  case 'ruflo':
@@ -146,7 +146,7 @@ export interface EvolutionConfig {
146
146
  }
147
147
  export interface Adapter {
148
148
  name: string;
149
- type: 'openclaw' | 'claude-code' | 'ruflo' | 'langchain' | 'autogen' | 'custom' | 'mcp' | 'codex' | 'opencode' | 'cursor' | 'windsurf' | 'aider' | 'continue' | 'cline';
149
+ type: 'openclaw' | 'hermes' | 'nanoclaw' | 'picoclaw' | 'claude-code' | 'ruflo' | 'langchain' | 'autogen' | 'custom' | 'mcp' | 'codex' | 'opencode' | 'cursor' | 'windsurf' | 'aider' | 'continue' | 'cline';
150
150
  connected: boolean;
151
151
  agents: string[];
152
152
  connect(): Promise<void>;
@@ -17,13 +17,11 @@
17
17
  /** UserPromptSubmit — call nexus_session_bootstrap so the model never has to. */
18
18
  export declare function runHookBootstrap(): Promise<void>;
19
19
  /**
20
- * PostToolUse (Edit|Write|MultiEdit|Bash) — opt-in audit memory.
20
+ * PostToolUse (Edit|Write|MultiEdit|Bash) — compact audit memory.
21
21
  *
22
- * Disabled by default because every Edit/Write/Bash was being stored as a
23
- * low-priority memory, which polluted recall results with audit-log entries
24
- * like "Edit: file_path=src/foo.ts". Real findings should be stored via
25
- * nexus_store_memory by the agent itself. To opt back in, set
26
- * NEXUS_HOOK_AUTO_MEMORY=1.
22
+ * The hook stores only compact, hidden lifecycle breadcrumbs. Real findings
23
+ * should still be stored via nexus_store_memory by the agent itself. Set
24
+ * NEXUS_HOOK_AUTO_MEMORY=0 to disable this hook in a local session.
27
25
  */
28
26
  export declare function runHookMemory(): Promise<void>;
29
27
  /**
package/dist/cli/hook.js CHANGED
@@ -150,16 +150,14 @@ export async function runHookBootstrap() {
150
150
  }, 10_000);
151
151
  }
152
152
  /**
153
- * PostToolUse (Edit|Write|MultiEdit|Bash) — opt-in audit memory.
153
+ * PostToolUse (Edit|Write|MultiEdit|Bash) — compact audit memory.
154
154
  *
155
- * Disabled by default because every Edit/Write/Bash was being stored as a
156
- * low-priority memory, which polluted recall results with audit-log entries
157
- * like "Edit: file_path=src/foo.ts". Real findings should be stored via
158
- * nexus_store_memory by the agent itself. To opt back in, set
159
- * NEXUS_HOOK_AUTO_MEMORY=1.
155
+ * The hook stores only compact, hidden lifecycle breadcrumbs. Real findings
156
+ * should still be stored via nexus_store_memory by the agent itself. Set
157
+ * NEXUS_HOOK_AUTO_MEMORY=0 to disable this hook in a local session.
160
158
  */
161
159
  export async function runHookMemory() {
162
- if (process.env.NEXUS_HOOK_AUTO_MEMORY !== '1')
160
+ if (process.env.NEXUS_HOOK_AUTO_MEMORY === '0')
163
161
  return;
164
162
  const data = await readStdinJson();
165
163
  const cwd = String(data.cwd ?? process.cwd());
@@ -182,7 +180,7 @@ export async function runHookMemory() {
182
180
  await callDaemonTool(lock, 'nexus_store_memory', {
183
181
  content: `${toolName}: ${inputSummary}`,
184
182
  priority: 0.2,
185
- tags: ['#auto', `tool:${toolName}`, `repo:${repoName}`],
183
+ tags: ['#auto', '#memory-hook', '#system-hidden', `tool:${toolName}`, `repo:${repoName}`],
186
184
  }, 5_000);
187
185
  }
188
186
  /**
@@ -37,6 +37,10 @@ const SETUP_CLIENT_BY_IDE = {
37
37
  windsurf: 'windsurf',
38
38
  continue: 'continue',
39
39
  cline: 'cline',
40
+ openclaw: 'openclaw',
41
+ hermes: 'hermes',
42
+ nanoclaw: 'nanoclaw',
43
+ picoclaw: 'picoclaw',
40
44
  codex: 'codex',
41
45
  };
42
46
  function resolveSetupWorkspaceRoot(explicitWorkspaceRoot) {
@@ -365,7 +369,7 @@ function mergeIntoExistingConfig(configPath, newContent, ide) {
365
369
  try {
366
370
  const existing = JSON.parse(readFileSync(configPath, 'utf8'));
367
371
  const incoming = JSON.parse(newContent);
368
- if (ide === 'cursor' || ide === 'windsurf' || ide === 'continue') {
372
+ if (ide === 'cursor' || ide === 'windsurf' || ide === 'continue' || ide === 'openclaw' || ide === 'hermes' || ide === 'nanoclaw' || ide === 'picoclaw') {
369
373
  // These use { mcpServers: { "nexus-prime": {...} } }
370
374
  existing.mcpServers = {
371
375
  ...(existing.mcpServers ?? {}),