nexus-prime 7.9.21 → 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 (59) 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/memory.js +44 -5
  6. package/dist/agents/adapters/mcp/handlers/orchestration.js +91 -0
  7. package/dist/agents/adapters/mcp/runHandler.js +3 -0
  8. package/dist/agents/adapters/mcp/util/detect-caller.js +21 -0
  9. package/dist/agents/adapters/mcp.js +1 -1
  10. package/dist/agents/adapters.d.ts +10 -1
  11. package/dist/agents/adapters.js +21 -0
  12. package/dist/agents/core/types.d.ts +1 -1
  13. package/dist/cli/hook.d.ts +4 -6
  14. package/dist/cli/hook.js +6 -8
  15. package/dist/cli/install-wizard.js +5 -1
  16. package/dist/cli.js +181 -15
  17. package/dist/core/types.d.ts +1 -1
  18. package/dist/dashboard/app/styles/board.css +85 -1
  19. package/dist/dashboard/app/styles/runtime.css +148 -0
  20. package/dist/dashboard/app/styles/workforce.css +28 -0
  21. package/dist/dashboard/app/views/board.js +56 -0
  22. package/dist/dashboard/app/views/memory.js +71 -10
  23. package/dist/dashboard/app/views/runtime.js +138 -4
  24. package/dist/dashboard/app/views/workforce.js +11 -4
  25. package/dist/dashboard/routes/events.js +3 -0
  26. package/dist/dashboard/selectors/operate-selector.js +5 -0
  27. package/dist/dashboard/selectors/runs-selector.js +5 -0
  28. package/dist/dashboard/server.js +6 -0
  29. package/dist/dashboard/types.d.ts +4 -0
  30. package/dist/engines/client-bootstrap.d.ts +5 -1
  31. package/dist/engines/client-bootstrap.js +105 -10
  32. package/dist/engines/client-registry.js +51 -0
  33. package/dist/engines/event-bus.d.ts +20 -2
  34. package/dist/engines/feature-registry.js +1 -0
  35. package/dist/engines/instruction-gateway.d.ts +9 -0
  36. package/dist/engines/instruction-gateway.js +113 -4
  37. package/dist/engines/memory/types.d.ts +28 -0
  38. package/dist/engines/memory-bridge.d.ts +1 -1
  39. package/dist/engines/memory-bridge.js +1 -1
  40. package/dist/engines/memory.d.ts +5 -0
  41. package/dist/engines/memory.js +144 -12
  42. package/dist/engines/orchestrator/decision-spine.d.ts +26 -0
  43. package/dist/engines/orchestrator/decision-spine.js +145 -6
  44. package/dist/engines/orchestrator/funnel.js +8 -1
  45. package/dist/engines/orchestrator/scoring.d.ts +1 -1
  46. package/dist/engines/orchestrator/scoring.js +24 -2
  47. package/dist/engines/orchestrator.d.ts +3 -0
  48. package/dist/engines/orchestrator.js +73 -13
  49. package/dist/engines/peer-connectors.d.ts +1 -1
  50. package/dist/engines/peer-connectors.js +9 -2
  51. package/dist/engines/runtime-registry.d.ts +9 -0
  52. package/dist/index.js +9 -0
  53. package/dist/install/state-locator.d.ts +1 -1
  54. package/dist/install/state-locator.js +3 -0
  55. package/dist/synapse/bootstrap.js +3 -0
  56. package/dist/synapse/mandate/pipeline.js +52 -5
  57. package/dist/synapse/sorties/runner.js +32 -0
  58. package/dist/synapse/types.d.ts +27 -0
  59. package/package.json +1 -1
@@ -35,6 +35,15 @@ import { resolveWorkspaceContext } from './workspace-resolver.js';
35
35
  import { resolveWorkspaceStateRoot } from './workspace-resolver.js';
36
36
  import { bootstrapFast, hasValidBootstrapReceipt, writeBootstrapReceipt, buildRepoIdentity, } from './bootstrap/index.js';
37
37
  import { GhostPass, createSubAgentRuntime, } from '../phantom/index.js';
38
+ function deferNexusEvent(type, payload) {
39
+ const handle = setImmediate(() => {
40
+ try {
41
+ nexusEventBus.emit(type, payload);
42
+ }
43
+ catch { /* advisory telemetry must not block orchestration */ }
44
+ });
45
+ handle.unref?.();
46
+ }
38
47
  // ─── Types and constants (extracted to orchestrator/types.ts) ─────────────────
39
48
  export * from './orchestrator/types.js';
40
49
  import { MAX_AUTONOMY_HISTORY, MAX_DISCOVERED_FILES, DISCOVERY_EXTENSIONS, DISCOVERY_IGNORES, DISCOVERY_FILENAME_SKIPS, DISCOVERY_BINARY_EXTENSIONS, DISCOVERY_DEFAULT_MAX_FILE_BYTES, } from './orchestrator/types.js';
@@ -81,6 +90,8 @@ export class OrchestratorEngine {
81
90
  workflowCatalogCache;
82
91
  hookCatalogCache;
83
92
  automationCatalogCache;
93
+ clientSnapshotCache;
94
+ CLIENT_SNAPSHOT_CACHE_TTL_MS = 1_000;
84
95
  specialistCatalogItems;
85
96
  crewCatalogItems;
86
97
  crGraphClient;
@@ -203,7 +214,7 @@ export class OrchestratorEngine {
203
214
  this.agents.set(id, agent);
204
215
  inductedAgents.push(agent);
205
216
  }
206
- nexusEventBus.emit('nexusnet.sync', { newItemsCount: inductedAgents.length });
217
+ deferNexusEvent('nexusnet.sync', { newItemsCount: inductedAgents.length });
207
218
  return inductedAgents;
208
219
  }
209
220
  decomposeTask(task) {
@@ -321,7 +332,7 @@ export class OrchestratorEngine {
321
332
  });
322
333
  const selections = await this.resolveSelections(task, intent, planner, knowledgeFabric, options);
323
334
  const catalogHealth = this.scanCatalogHealth(selections);
324
- const tokenBudget = toSourceAwareTokenBudget(knowledgeFabric, plannedFiles, 'knowledge-fabric-source-aware-budget');
335
+ const tokenBudget = toSourceAwareTokenBudget(knowledgeFabric, plannedFiles, 'knowledge-fabric-source-aware-budget', task);
325
336
  const workerCount = decideWorkers(options.workers, planner.swarmDecision.workers, phases.length, intent, this.sessionState.repeatedFailures, knowledgeFabric);
326
337
  const mode = determineMode(intent, phases.length, workerCount, this.sessionState.repeatedFailures);
327
338
  const taskGraph = buildTaskGraph(task, phases, intent);
@@ -1245,6 +1256,8 @@ export class OrchestratorEngine {
1245
1256
  estimatedCompressionPct: tokenPlan && tokenPlan.totalEstimatedTokens + tokenPlan.savings > 0
1246
1257
  ? Math.round((tokenPlan.savings / (tokenPlan.totalEstimatedTokens + tokenPlan.savings)) * 100)
1247
1258
  : 0,
1259
+ codeBlockTemplates: tokenBudget.codeBlocks?.templates,
1260
+ codeBlockReservedTokens: tokenBudget.codeBlocks?.reservedTokens,
1248
1261
  },
1249
1262
  memoryMatches,
1250
1263
  memoryStats,
@@ -1589,19 +1602,27 @@ export class OrchestratorEngine {
1589
1602
  return RunManagement.pruneStaleAutonomySessions(this._runMgrCtx());
1590
1603
  }
1591
1604
  resolvePrimaryClient() {
1592
- const primary = this.clientRegistry?.getPrimaryClient?.();
1593
- if (primary)
1594
- return this.toClientSnapshot(primary);
1595
- const detected = this.detectCurrentClientFromEnv();
1596
- return detected ? this.toClientSnapshot(detected) : undefined;
1605
+ return this.getClientSnapshot().primary;
1597
1606
  }
1598
1607
  listDetectedClients() {
1599
- const clients = this.clientRegistry?.listClients?.() ?? [];
1600
- if (clients.length === 0) {
1601
- const detected = this.detectCurrentClientFromEnv();
1602
- return detected ? [this.toClientSnapshot(detected)] : [];
1603
- }
1604
- return clients.map((client) => this.toClientSnapshot(client));
1608
+ return this.getClientSnapshot().clients;
1609
+ }
1610
+ getClientSnapshot() {
1611
+ const now = Date.now();
1612
+ if (this.clientSnapshotCache && now - this.clientSnapshotCache.ts < this.CLIENT_SNAPSHOT_CACHE_TTL_MS) {
1613
+ return this.clientSnapshotCache;
1614
+ }
1615
+ const records = this.clientRegistry?.listClients?.() ?? [];
1616
+ const clients = records.length > 0
1617
+ ? records.map((client) => this.toClientSnapshot(client))
1618
+ : (() => {
1619
+ const detected = this.detectCurrentClientFromEnv();
1620
+ return detected ? [this.toClientSnapshot(detected)] : [];
1621
+ })();
1622
+ const primary = clients.find((client) => client.state === 'primaryActive')
1623
+ ?? clients.find((client) => client.state !== 'offline');
1624
+ this.clientSnapshotCache = { ts: now, primary, clients };
1625
+ return this.clientSnapshotCache;
1605
1626
  }
1606
1627
  detectCurrentClientFromEnv() {
1607
1628
  if (process.env.CODEX_HOME || process.env.CODEX_SESSION) {
@@ -1669,6 +1690,45 @@ export class OrchestratorEngine {
1669
1690
  lastSeen: Date.now(),
1670
1691
  };
1671
1692
  }
1693
+ if (process.env.HERMES_HOME || process.env.HERMES_SESSION) {
1694
+ return {
1695
+ clientId: 'hermes',
1696
+ displayName: 'Hermes',
1697
+ state: 'primaryActive',
1698
+ source: 'manual',
1699
+ inferred: false,
1700
+ confidence: 1,
1701
+ evidence: ['env:HERMES detected'],
1702
+ metadata: {},
1703
+ lastSeen: Date.now(),
1704
+ };
1705
+ }
1706
+ if (process.env.NANOCLAW_HOME || process.env.NANOCLAW_SESSION) {
1707
+ return {
1708
+ clientId: 'nanoclaw',
1709
+ displayName: 'NanoClaw',
1710
+ state: 'primaryActive',
1711
+ source: 'manual',
1712
+ inferred: false,
1713
+ confidence: 1,
1714
+ evidence: ['env:NANOCLAW detected'],
1715
+ metadata: {},
1716
+ lastSeen: Date.now(),
1717
+ };
1718
+ }
1719
+ if (process.env.PICOCLAW_HOME || process.env.PICOCLAW_SESSION) {
1720
+ return {
1721
+ clientId: 'picoclaw',
1722
+ displayName: 'PicoClaw',
1723
+ state: 'primaryActive',
1724
+ source: 'manual',
1725
+ inferred: false,
1726
+ confidence: 1,
1727
+ evidence: ['env:PICOCLAW detected'],
1728
+ metadata: {},
1729
+ lastSeen: Date.now(),
1730
+ };
1731
+ }
1672
1732
  if (process.env.WINDSURF_HOME || process.env.WINDSURF_SESSION) {
1673
1733
  return {
1674
1734
  clientId: 'windsurf',
@@ -29,7 +29,7 @@ export interface PeerProfile {
29
29
  export declare function detectAtlasFoundation(): AtlasFoundationState;
30
30
  /**
31
31
  * Scans standard paths in the user's home directory to dynamically
32
- * discover peer AI agent ecosystems like OpenClaw, Hermes, PicoClaw, or Atlas.
32
+ * discover peer AI agent ecosystems like OpenClaw, Hermes, NanoClaw, PicoClaw, or Atlas.
33
33
  *
34
34
  * @returns Array of detected peer profiles
35
35
  */
@@ -64,24 +64,31 @@ export function detectAtlasFoundation() {
64
64
  }
65
65
  /**
66
66
  * Scans standard paths in the user's home directory to dynamically
67
- * discover peer AI agent ecosystems like OpenClaw, Hermes, PicoClaw, or Atlas.
67
+ * discover peer AI agent ecosystems like OpenClaw, Hermes, NanoClaw, PicoClaw, or Atlas.
68
68
  *
69
69
  * @returns Array of detected peer profiles
70
70
  */
71
71
  export async function detectAllPeers() {
72
72
  const home = os.homedir();
73
73
  const peers = [];
74
+ const seenPeerIds = new Set();
74
75
  const candidates = [
75
76
  { id: 'openclaw', relativeConfig: '.openclaw/openclaw.json' },
76
77
  { id: 'hermes', relativeConfig: '.hermes/config.yaml' },
78
+ { id: 'hermes', relativeConfig: '.hermes/mcp.json' },
79
+ { id: 'nanoclaw', relativeConfig: '.nanoclaw/mcp.json' },
77
80
  { id: 'picoclaw', relativeConfig: '.picoclaw/config.yaml' },
81
+ { id: 'picoclaw', relativeConfig: '.picoclaw/mcp.json' },
78
82
  { id: 'opencode', relativeConfig: '.config/opencode/opencode.json' },
79
83
  ];
80
84
  for (const c of candidates) {
81
85
  const fullPath = path.join(home, c.relativeConfig);
82
86
  try {
83
87
  if (fs.existsSync(fullPath)) {
84
- const capabilities = ['skill-sharing', 'memory-sync', 'tool-relay'];
88
+ if (seenPeerIds.has(c.id))
89
+ continue;
90
+ seenPeerIds.add(c.id);
91
+ const capabilities = ['skill-sharing', 'memory-sync', 'tool-relay', 'token-budgeting', 'workforce-hiring'];
85
92
  peers.push({
86
93
  id: c.id,
87
94
  configPath: fullPath,
@@ -194,6 +194,15 @@ export interface RuntimeSourceAwareTokenBudgetSnapshot {
194
194
  reason: string;
195
195
  }>;
196
196
  dominantSource?: string;
197
+ codeBlocks?: {
198
+ requested: boolean;
199
+ detectedBlocks: number;
200
+ reservedTokens: number;
201
+ templates: string[];
202
+ languages: string[];
203
+ };
204
+ qualityGates?: string[];
205
+ recommendations?: string[];
197
206
  }
198
207
  export interface RuntimeTaskGraphPhase {
199
208
  id: string;
package/dist/index.js CHANGED
@@ -1192,6 +1192,15 @@ function detectCurrentClient() {
1192
1192
  if (process.env.OPENCLAW_HOME || process.env.ANTIGRAVITY_HOME) {
1193
1193
  return { clientId: 'antigravity', source: 'env' };
1194
1194
  }
1195
+ if (process.env.HERMES_HOME || process.env.HERMES_SESSION) {
1196
+ return { clientId: 'hermes', source: 'env' };
1197
+ }
1198
+ if (process.env.NANOCLAW_HOME || process.env.NANOCLAW_SESSION) {
1199
+ return { clientId: 'nanoclaw', source: 'env' };
1200
+ }
1201
+ if (process.env.PICOCLAW_HOME || process.env.PICOCLAW_SESSION) {
1202
+ return { clientId: 'picoclaw', source: 'env' };
1203
+ }
1195
1204
  if (process.env.WINDSURF_HOME || process.env.WINDSURF_SESSION) {
1196
1205
  return { clientId: 'windsurf', source: 'env' };
1197
1206
  }
@@ -36,7 +36,7 @@ export interface NgramArchiveEntry {
36
36
  export declare function enumerateNgramArchives(stateDir?: string): NgramArchiveEntry[];
37
37
  /** Known IDE registration targets discovered in a workspace + home dir. */
38
38
  export interface RegistrationTarget {
39
- id: 'aider' | 'antigravity' | 'claude-desktop' | 'claude-home' | 'claude-workspace' | 'cline' | 'codex-json' | 'codex-toml' | 'continue' | 'cursor-home' | 'cursor-workspace' | 'opencode' | 'openclaw' | 'vscode-workspace' | 'windsurf';
39
+ id: 'aider' | 'antigravity' | 'claude-desktop' | 'claude-home' | 'claude-workspace' | 'cline' | 'codex-json' | 'codex-toml' | 'continue' | 'cursor-home' | 'cursor-workspace' | 'hermes' | 'nanoclaw' | 'opencode' | 'openclaw' | 'picoclaw' | 'vscode-workspace' | 'windsurf';
40
40
  filePath: string;
41
41
  jsonPath: string[];
42
42
  }
@@ -136,6 +136,9 @@ export function enumerateRegistrationTargets(workspaceRoot, home = os.homedir())
136
136
  { id: 'windsurf', filePath: path.join(home, '.windsurf', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
137
137
  { id: 'antigravity', filePath: path.join(home, '.antigravity', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
138
138
  { id: 'openclaw', filePath: path.join(home, '.openclaw', 'openclaw.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
139
+ { id: 'hermes', filePath: path.join(home, '.hermes', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
140
+ { id: 'nanoclaw', filePath: path.join(home, '.nanoclaw', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
141
+ { id: 'picoclaw', filePath: path.join(home, '.picoclaw', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
139
142
  { id: 'aider', filePath: path.join(home, '.aider', 'mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
140
143
  { id: 'continue', filePath: path.join(home, '.continue', 'config.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
141
144
  { id: 'cline', filePath: path.join(home, '.vscode', 'cline-mcp.json'), jsonPath: ['mcpServers', 'nexus-prime'] },
@@ -86,6 +86,9 @@ function hireOperativeDirect(db, providers, input) {
86
86
  operativeId: operative.id,
87
87
  name: operative.name,
88
88
  skillId: operative.skillId,
89
+ specialistId: operative.specialistId,
90
+ roleTitle: operative.roleTitle,
91
+ reportsToOperativeId: operative.reportsToOperativeId,
89
92
  strikeTeamId: operative.strikeTeamId,
90
93
  });
91
94
  storeOperationalMemory(providers, `[Synapse:Hire] ${operative.name} hired as ${operative.roleTitle ?? 'Operative'}`, ['#hire'], {
@@ -10,6 +10,17 @@ import { safePublish } from '../coordination/safe-publish.js';
10
10
  function unique(values) {
11
11
  return [...new Set(values)];
12
12
  }
13
+ function boundedRequestedOperatives(opts) {
14
+ const requested = Number(opts.maxOperatives ?? SynapseConfig.maxOpsPerTeam);
15
+ const safeRequested = Number.isFinite(requested) && requested > 0 ? Math.floor(requested) : SynapseConfig.maxOpsPerTeam;
16
+ return Math.max(1, Math.min(safeRequested, SynapseConfig.maxOpsPerTeam));
17
+ }
18
+ function decideMandateOperativeCount(signals, matched, missionTitles, opts) {
19
+ const requested = boundedRequestedOperatives(opts);
20
+ const evidenceCount = Math.max(matched.skills.length, matched.specialists.length, missionTitles.length);
21
+ const complexityFloor = signals.complexity === 'read' ? 1 : 2;
22
+ return Math.max(1, Math.min(requested, Math.max(evidenceCount || 1, complexityFloor)));
23
+ }
13
24
  function deriveMissionTitles(mandateText, complexity, hints, planner) {
14
25
  const plannerHints = [
15
26
  ...planner.plannerState.selectedSpecialists.map((specialist) => `Coordinate with ${specialist.name}`),
@@ -29,7 +40,7 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
29
40
  const planner = planTask({
30
41
  goal: mandateText,
31
42
  files: [],
32
- workers: Math.max(2, Math.min(opts.maxOperatives ?? SynapseConfig.maxOpsPerTeam, SynapseConfig.maxOpsPerTeam)),
43
+ workers: Math.max(2, boundedRequestedOperatives(opts)),
33
44
  roles: ['planner', 'coder'],
34
45
  strategies: ['standard'],
35
46
  verifyCommands: [],
@@ -63,7 +74,7 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
63
74
  manualOverrides: [],
64
75
  });
65
76
  const missionTitles = deriveMissionTitles(mandateText, signals.complexity, signals.subGoalHints, planner);
66
- const maxOps = Math.min(opts.maxOperatives ?? SynapseConfig.maxOpsPerTeam, Math.max(1, matched.skills.length || 1));
77
+ const maxOps = decideMandateOperativeCount(signals, matched, missionTitles, opts);
67
78
  const budget = opts.budgetUsd ?? SynapseConfig.defaultBudgetUsd;
68
79
  const team = db.transaction(() => {
69
80
  const teamId = randomUUID();
@@ -71,9 +82,11 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
71
82
  const worklistId = providers.coordination?.getWorklistId(teamId) ?? null;
72
83
  const blueprintId = worklistId ? `implicit-blueprint:${teamId}` : null;
73
84
  let leadOperativeId = null;
85
+ const hiringPlan = [];
86
+ const missionPlan = [];
74
87
  const operativeIds = Array.from({ length: maxOps }, (_, index) => {
75
- const skill = matched.skills[index] ?? matched.skills[0];
76
- const specialist = matched.specialists[index] ?? matched.specialists[0] ?? null;
88
+ const skill = matched.skills.length > 0 ? matched.skills[index % matched.skills.length] : undefined;
89
+ const specialist = matched.specialists.length > 0 ? matched.specialists[index % matched.specialists.length] : null;
77
90
  const operative = insertOperative(db, {
78
91
  id: randomUUID(),
79
92
  name: `operative-${teamId.slice(0, 4)}-${index + 1}`,
@@ -89,10 +102,22 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
89
102
  });
90
103
  if (index === 0)
91
104
  leadOperativeId = operative.id;
105
+ hiringPlan.push({
106
+ operativeId: operative.id,
107
+ name: operative.name,
108
+ roleTitle: operative.roleTitle,
109
+ skillId: operative.skillId,
110
+ specialistId: operative.specialistId,
111
+ reportsToOperativeId: operative.reportsToOperativeId,
112
+ budgetCapUsd: operative.budgetCapUsd,
113
+ });
92
114
  nexusEventBus.emit('synapse.operative.hired', {
93
115
  operativeId: operative.id,
94
116
  name: operative.name,
95
117
  skillId: operative.skillId,
118
+ specialistId: operative.specialistId,
119
+ roleTitle: operative.roleTitle,
120
+ reportsToOperativeId: operative.reportsToOperativeId,
96
121
  strikeTeamId: teamId,
97
122
  });
98
123
  return operative.id;
@@ -115,6 +140,13 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
115
140
  budgetScopeId: teamId,
116
141
  });
117
142
  updateOperativeMission(db, operativeId, mission.id, teamId);
143
+ missionPlan.push({
144
+ missionId: mission.id,
145
+ title: mission.title,
146
+ assignedOperativeId: operativeId,
147
+ complexity: mission.complexity,
148
+ requiresApprovalGate: mission.requiresApprovalGate,
149
+ });
118
150
  nexusEventBus.emit('synapse.mission.assigned', {
119
151
  operativeId,
120
152
  missionId: mission.id,
@@ -135,6 +167,15 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
135
167
  }, 'mandate');
136
168
  return mission.id;
137
169
  });
170
+ const selectionSummary = {
171
+ domains: signals.domains,
172
+ complexity: signals.complexity,
173
+ skills: unique(matched.skills.slice(0, maxOps).map((skill) => skill.name).filter(Boolean)),
174
+ specialists: unique(matched.specialists.slice(0, maxOps).map((specialist) => specialist.name).filter(Boolean)),
175
+ workflows: unique(planner.plannerState.selectedWorkflows),
176
+ operativeCount: operativeIds.length,
177
+ missionCount: missionIds.length,
178
+ };
138
179
  insertStrikeTeam(db, {
139
180
  id: teamId,
140
181
  mandateText,
@@ -147,6 +188,7 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
147
188
  strikeTeamId: teamId,
148
189
  operativeCount: operativeIds.length,
149
190
  missionCount: missionIds.length,
191
+ selectionSummary,
150
192
  worklistId,
151
193
  correlationId: teamId,
152
194
  });
@@ -164,7 +206,12 @@ export async function executeMandatePipeline(db, mandateText, providers, opts =
164
206
  containerTags: [`#team:${teamId}`, ...(worklistId ? [`#worklist:${worklistId}`] : [])],
165
207
  },
166
208
  });
167
- return getStrikeTeam(db, teamId);
209
+ return {
210
+ ...getStrikeTeam(db, teamId),
211
+ selectionSummary,
212
+ hiringPlan,
213
+ missionPlan,
214
+ };
168
215
  })();
169
216
  nexusEventBus.emit('synapse.striketeam.finished', {
170
217
  strikeTeamId: team.id,
@@ -57,6 +57,15 @@ function createSyntheticSortie(db, operative, mission, status) {
57
57
  completedAt: new Date().toISOString(),
58
58
  });
59
59
  }
60
+ function extractWorkflowSelectorsFromMission(title) {
61
+ const selectors = new Set();
62
+ for (const match of title.matchAll(/Execute workflow:\s*([^.;\n]+)/gi)) {
63
+ const selector = match[1]?.trim();
64
+ if (selector)
65
+ selectors.add(selector);
66
+ }
67
+ return [...selectors];
68
+ }
60
69
  export async function runSortie(db, operative, providers) {
61
70
  const mission = getMission(db, operative.missionId);
62
71
  if (!mission) {
@@ -138,10 +147,33 @@ export async function runSortie(db, operative, providers) {
138
147
  });
139
148
  }
140
149
  try {
150
+ const workflowSelectors = extractWorkflowSelectorsFromMission(mission.title);
151
+ nexusEventBus.emit('synapse.sortie.selection', {
152
+ sortieId: sortie.id,
153
+ operativeId: operative.id,
154
+ missionId: mission.id,
155
+ strikeTeamId: mission.strikeTeamId,
156
+ skillId: operative.skillId,
157
+ specialistId: operative.specialistId,
158
+ workflowSelectors,
159
+ workerCount: 1,
160
+ });
161
+ safePublish(providers.coordination, {
162
+ phase: 'sortie',
163
+ summary: `Selection for ${mission.title}: skill=${operative.skillId ?? 'none'} specialist=${operative.specialistId ?? 'none'} workflows=${workflowSelectors.join(', ') || 'none'}`,
164
+ strikeTeamId: mission.strikeTeamId,
165
+ worklistId,
166
+ workItemId: mission.id,
167
+ operativeId: operative.id,
168
+ sortieId: sortie.id,
169
+ correlationId,
170
+ status: 'selected',
171
+ });
141
172
  const execution = await providers.orchestrator.orchestrate(goal, {
142
173
  files: [],
143
174
  workers: 1,
144
175
  skillNames: operative.skillId ? [operative.skillId] : [],
176
+ workflowSelectors,
145
177
  specialistSelectors: operative.specialistId ? [operative.specialistId] : [],
146
178
  executionMode: 'autonomous',
147
179
  });
@@ -61,6 +61,22 @@ export interface OperativeUpdateInput {
61
61
  strikeTeamId?: string | null;
62
62
  reason?: string | null;
63
63
  }
64
+ export interface StrikeTeamHiringPlanEntry {
65
+ operativeId: string;
66
+ name: string;
67
+ roleTitle: string | null;
68
+ skillId: string | null;
69
+ specialistId: string | null;
70
+ reportsToOperativeId: string | null;
71
+ budgetCapUsd: number;
72
+ }
73
+ export interface StrikeTeamMissionPlanEntry {
74
+ missionId: string;
75
+ title: string;
76
+ assignedOperativeId: string;
77
+ complexity: MissionComplexity;
78
+ requiresApprovalGate: boolean;
79
+ }
64
80
  export interface StrikeTeam {
65
81
  id: string;
66
82
  mandateText: string;
@@ -69,6 +85,17 @@ export interface StrikeTeam {
69
85
  blueprintId: string | null;
70
86
  status: 'deploying' | 'active' | 'converging' | 'done' | 'standdown';
71
87
  createdAt: string;
88
+ selectionSummary?: {
89
+ domains: string[];
90
+ complexity: MissionComplexity;
91
+ skills: string[];
92
+ specialists: string[];
93
+ workflows: string[];
94
+ operativeCount: number;
95
+ missionCount: number;
96
+ };
97
+ hiringPlan?: StrikeTeamHiringPlanEntry[];
98
+ missionPlan?: StrikeTeamMissionPlanEntry[];
72
99
  }
73
100
  export interface Mission {
74
101
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-prime",
3
- "version": "7.9.21",
3
+ "version": "7.9.23",
4
4
  "description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",