nexus-prime 7.9.6 → 7.9.8

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.
@@ -35,6 +35,8 @@ export declare class SessionTelemetry {
35
35
  private fileIntentPaths;
36
36
  private tokenAutoApplied;
37
37
  private stickyTokenOptimization;
38
+ private tokenOptimizationPending;
39
+ private orchestrationPending;
38
40
  bootstrapped: boolean;
39
41
  bootstrapCallCount: number;
40
42
  recordCall(): void;
@@ -54,12 +56,16 @@ export declare class SessionTelemetry {
54
56
  fileReadIntentCount: number;
55
57
  callsSinceOrchestrate: number;
56
58
  tokenAutoApplied: boolean;
59
+ tokenOptimizationPending: boolean;
60
+ orchestrationPending: boolean;
57
61
  };
58
62
  elapsedMs(): number;
59
63
  advancePhase(nextPhase: LifecyclePhase): void;
60
64
  observeSuccessfulToolCall(toolName: string, args: Record<string, unknown>): void;
65
+ observeQueuedToolCall(toolName: string, args: Record<string, unknown>): void;
61
66
  needsOptimizeTokens(currentToolName?: string): boolean;
62
67
  markTokenAutoApplied(): void;
68
+ needsOrchestration(currentToolName?: string): boolean;
63
69
  needsStoreMemory(currentToolName?: string): boolean;
64
70
  needsSessionDna(currentToolName?: string): boolean;
65
71
  notifyStore(priority: number, tags: string[], memStats: {
@@ -24,6 +24,8 @@ export class SessionTelemetry {
24
24
  fileIntentPaths = new Set();
25
25
  tokenAutoApplied = false;
26
26
  stickyTokenOptimization = false;
27
+ tokenOptimizationPending = false;
28
+ orchestrationPending = false;
27
29
  bootstrapped = false;
28
30
  bootstrapCallCount = 0;
29
31
  recordCall() {
@@ -49,6 +51,8 @@ export class SessionTelemetry {
49
51
  fileReadIntentCount: this.fileReadIntentCount,
50
52
  callsSinceOrchestrate: this.callsSinceOrchestrate,
51
53
  tokenAutoApplied: this.tokenAutoApplied,
54
+ tokenOptimizationPending: this.tokenOptimizationPending,
55
+ orchestrationPending: this.orchestrationPending,
52
56
  };
53
57
  }
54
58
  elapsedMs() {
@@ -69,8 +73,10 @@ export class SessionTelemetry {
69
73
  this.optimizeTokensCalled = false;
70
74
  this.tokenAutoApplied = false;
71
75
  }
76
+ this.tokenOptimizationPending = false;
72
77
  }
73
78
  if (nextPhase === 'orchestrated') {
79
+ this.orchestrationPending = false;
74
80
  this.mindkitCheckCalled = false;
75
81
  this.storeMemoryCalledPostOrchestrate = false;
76
82
  this.sessionDnaCalled = false;
@@ -85,12 +91,17 @@ export class SessionTelemetry {
85
91
  return;
86
92
  }
87
93
  if (toolName === 'nexus_orchestrate') {
94
+ this.stickyTokenOptimization = true;
88
95
  this.advancePhase('orchestrated');
96
+ this.optimizeTokensCalled = true;
97
+ this.tokenAutoApplied = true;
98
+ this.tokenOptimizationPending = false;
89
99
  this.noteFileIntent(args.files);
90
100
  return;
91
101
  }
92
102
  if (toolName === 'nexus_optimize_tokens') {
93
103
  this.optimizeTokensCalled = true;
104
+ this.tokenOptimizationPending = false;
94
105
  if (this.lifecyclePhase === 'orchestrated')
95
106
  this.advancePhase('working');
96
107
  this.noteFileIntent(args.files);
@@ -126,6 +137,20 @@ export class SessionTelemetry {
126
137
  this.advancePhase('working');
127
138
  }
128
139
  }
140
+ observeQueuedToolCall(toolName, args) {
141
+ if (toolName === 'nexus_orchestrate') {
142
+ this.orchestrationPending = true;
143
+ this.tokenOptimizationPending = true;
144
+ this.optimizeTokensCalled = true;
145
+ this.noteFileIntent(args.files);
146
+ return;
147
+ }
148
+ if (toolName === 'nexus_optimize_tokens') {
149
+ this.tokenOptimizationPending = true;
150
+ this.optimizeTokensCalled = true;
151
+ this.noteFileIntent(args.files);
152
+ }
153
+ }
129
154
  needsOptimizeTokens(currentToolName) {
130
155
  if (currentToolName === 'nexus_optimize_tokens')
131
156
  return false;
@@ -133,12 +158,26 @@ export class SessionTelemetry {
133
158
  return false;
134
159
  if (this.tokenAutoApplied)
135
160
  return false;
161
+ if (this.tokenOptimizationPending)
162
+ return false;
136
163
  return this.fileReadIntentCount >= 1 && !this.optimizeTokensCalled;
137
164
  }
138
165
  markTokenAutoApplied() {
139
166
  this.tokenAutoApplied = true;
140
167
  this.optimizeTokensCalled = true;
141
168
  this.stickyTokenOptimization = true;
169
+ this.tokenOptimizationPending = false;
170
+ }
171
+ needsOrchestration(currentToolName) {
172
+ if (currentToolName === 'nexus_orchestrate')
173
+ return false;
174
+ if (this.lifecyclePhase === 'pre-bootstrap')
175
+ return false;
176
+ if (this.lifecyclePhase === 'orchestrated' || this.lifecyclePhase === 'working' || this.lifecyclePhase === 'closing')
177
+ return false;
178
+ if (this.orchestrationPending)
179
+ return false;
180
+ return this.bootstrapped && this.lifecyclePhase === 'bootstrapped';
142
181
  }
143
182
  needsStoreMemory(currentToolName) {
144
183
  if (currentToolName === 'nexus_store_memory')
@@ -184,9 +223,8 @@ export class SessionTelemetry {
184
223
  }
185
224
  break;
186
225
  case 'store':
187
- if ((context.priority ?? 0) > 0.8) {
188
- nudges.push('High-priority insight stored. Next: call nexus_store_memory to persist related findings.');
189
- }
226
+ // Storing memory completes the lifecycle step; do not create a
227
+ // self-referential "store again" loop.
190
228
  break;
191
229
  case 'mindkit_fail':
192
230
  nudges.push('Guardrail FAILED. Do NOT proceed. Re-scope the task or call nexus_ghost_pass for a safer approach.');
@@ -41,6 +41,7 @@ export declare class MCPAdapter implements Adapter {
41
41
  private formatRemainingProtocolSteps;
42
42
  private prependTextToResponse;
43
43
  private decorateLifecycleResponse;
44
+ private buildPreOrchestrationBlock;
44
45
  private extractGoalLikeText;
45
46
  private isMemoryTool;
46
47
  private injectMemoryContext;
@@ -124,6 +124,23 @@ const PRE_BOOTSTRAP_ALLOWED_TOOLS = new Set([
124
124
  'nexus_list_specialists',
125
125
  'nexus_list_crews',
126
126
  ]);
127
+ const PRE_ORCHESTRATE_ALLOWED_TOOLS = new Set([
128
+ ...PRE_BOOTSTRAP_ALLOWED_TOOLS,
129
+ 'nexus_orchestrate',
130
+ 'nexus_plan_execution',
131
+ 'nexus_recall_memory',
132
+ 'nexus_temporal_query',
133
+ 'nexus_store_memory',
134
+ 'nexus_search',
135
+ 'nexus_optimize_tokens',
136
+ 'nexus_mindkit_check',
137
+ 'nexus_ghost_pass',
138
+ 'nexus_token_report',
139
+ 'nexus_session_dna',
140
+ 'nexus_run_status',
141
+ 'nexus_selection_ledger',
142
+ 'nexus_license_usage',
143
+ ]);
127
144
  export class MCPAdapter {
128
145
  name;
129
146
  type = 'mcp';
@@ -513,6 +530,24 @@ export class MCPAdapter {
513
530
  ],
514
531
  };
515
532
  }
533
+ buildPreOrchestrationBlock(toolName) {
534
+ if (!this.telemetry.needsOrchestration(toolName))
535
+ return null;
536
+ if (PRE_ORCHESTRATE_ALLOWED_TOOLS.has(toolName) || isReadOnlyMcpTool(toolName))
537
+ return null;
538
+ return {
539
+ content: [{
540
+ type: 'text',
541
+ text: JSON.stringify({
542
+ status: 'blocked',
543
+ code: 'orchestration-required',
544
+ reason: 'nexus_orchestrate has not been called for this session',
545
+ action: 'Call nexus_orchestrate(prompt="<your task>") before invoking mutation, worker, kernel, hook, automation, or low-level execution tools.',
546
+ hint: 'Nexus Prime must own planning, token budgeting, memory recall, hooks, and review gates before work starts.',
547
+ }, null, 2),
548
+ }],
549
+ };
550
+ }
516
551
  extractGoalLikeText(args) {
517
552
  const candidates = [args.goal, args.task, args.prompt, args.query, args.action, args.content]
518
553
  .map((value) => String(value ?? '').trim())
@@ -790,6 +825,9 @@ export class MCPAdapter {
790
825
  }],
791
826
  };
792
827
  }
828
+ const preOrchestrationBlock = this.buildPreOrchestrationBlock(toolName);
829
+ if (preOrchestrationBlock)
830
+ return preOrchestrationBlock;
793
831
  const callId = `mcp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
794
832
  const startTimeMs = Date.now();
795
833
  nexusEventBus.emit('mcp.call.start', {
@@ -831,8 +869,13 @@ export class MCPAdapter {
831
869
  ? { result: resultPreview }
832
870
  : { error: envelope.error.message }),
833
871
  });
834
- if (envelope.ok && !asyncReceipt?.queued) {
835
- this.telemetry.observeSuccessfulToolCall(toolName, args);
872
+ if (envelope.ok) {
873
+ if (asyncReceipt?.queued) {
874
+ this.telemetry.observeQueuedToolCall(toolName, args);
875
+ }
876
+ else {
877
+ this.telemetry.observeSuccessfulToolCall(toolName, args);
878
+ }
836
879
  }
837
880
  // Surface meaningful tool calls on the SSE feed; persist only high-signal outcomes.
838
881
  if (this.nexusRef) {
@@ -1028,8 +1071,10 @@ export class MCPAdapter {
1028
1071
  return;
1029
1072
  // Skip if an explicit optimize/orchestrate already ran this session.
1030
1073
  const usage = this.nexusRef?.getRuntime?.()?.getUsageSnapshot?.();
1031
- if (usage?.tokenOptimizationApplied)
1074
+ if (usage?.tokenOptimizationApplied) {
1075
+ this.telemetry.markTokenAutoApplied?.();
1032
1076
  return;
1077
+ }
1033
1078
  const fileRefs = this.extractFileRefsFromArgs(args);
1034
1079
  if (fileRefs.length < 5)
1035
1080
  return;
@@ -1043,6 +1088,7 @@ export class MCPAdapter {
1043
1088
  const compressionRatio = grossInput > 0 ? totalEstimated / grossInput : 0;
1044
1089
  const pct = grossInput > 0 ? Math.round((savings / grossInput) * 100) : 0;
1045
1090
  // Mark applied so we don't repeat this for every subsequent tool call.
1091
+ this.telemetry.markTokenAutoApplied?.();
1046
1092
  try {
1047
1093
  this.nexusRef?.getRuntime?.()?.recordClientToolCall?.(toolName, {
1048
1094
  tokenOptimizationApplied: true,
package/dist/cli.js CHANGED
@@ -1329,12 +1329,14 @@ program
1329
1329
  .alias('claude')
1330
1330
  .description('Integrate with Claude Code')
1331
1331
  .option('--dry-run', 'Preview changes')
1332
- .option('--hooks', 'Also install Claude Code hooks into ~/.claude/settings.json for auto-bootstrap, auto-memory, and governance')
1332
+ .option('--hooks', 'Install Claude Code hooks into ~/.claude/settings.json for auto-bootstrap, auto-memory, and governance (default)')
1333
+ .option('--no-hooks', 'Skip Claude Code hooks')
1333
1334
  .action((options) => {
1334
1335
  const definition = getSetupDefinition('claude-code');
1336
+ const shouldInstallHooks = options.hooks !== false;
1335
1337
  if (options.dryRun) {
1336
1338
  printSetupPreview(definition);
1337
- if (options.hooks) {
1339
+ if (shouldInstallHooks) {
1338
1340
  const settingsPath = join(homedir(), '.claude', 'settings.json');
1339
1341
  writeClaudeCodeHooks(settingsPath, true);
1340
1342
  }
@@ -1344,7 +1346,7 @@ program
1344
1346
  console.log(`✅ Nexus Prime installed for Claude Code`);
1345
1347
  console.log(` MCP: ${definition.configPath}`);
1346
1348
  definition.instructionFiles.forEach((file) => console.log(` Instruction: ${file.path}`));
1347
- if (options.hooks) {
1349
+ if (shouldInstallHooks) {
1348
1350
  const settingsPath = join(homedir(), '.claude', 'settings.json');
1349
1351
  writeClaudeCodeHooks(settingsPath, false);
1350
1352
  console.log(`✅ Claude Code hooks installed (auto-bootstrap, auto-memory, governance)`);
@@ -1428,9 +1430,11 @@ program
1428
1430
  .map((clientId) => getSetupDefinition(clientId));
1429
1431
  if (options.dryRun) {
1430
1432
  definitions.forEach((definition) => printSetupPreview(definition));
1433
+ writeClaudeCodeHooks(join(homedir(), '.claude', 'settings.json'), true);
1431
1434
  return;
1432
1435
  }
1433
1436
  definitions.forEach((definition) => installSetup(definition));
1437
+ writeClaudeCodeHooks(join(homedir(), '.claude', 'settings.json'), false);
1434
1438
  console.log('✅ Nexus Prime installed for all supported clients');
1435
1439
  definitions.forEach((definition) => {
1436
1440
  console.log(` ${definition.label}`);
@@ -293,6 +293,9 @@ export function decideWorkers(requestedWorkers, plannedWorkers, phaseCount, inte
293
293
  if (typeof requestedWorkers === 'number' && requestedWorkers > 0) {
294
294
  return Math.max(1, Math.min(7, requestedWorkers));
295
295
  }
296
+ if (intent.taskType === 'release') {
297
+ return 1;
298
+ }
296
299
  if (intent.riskClass !== 'high'
297
300
  && intent.complexity <= 3
298
301
  && phaseCount <= 2
@@ -2316,6 +2316,23 @@ export class OrchestratorEngine {
2316
2316
  limit: 4,
2317
2317
  selector: 'name',
2318
2318
  });
2319
+ const selectedWorkflowValues = intent.taskType === 'release'
2320
+ ? dedupeStrings(['release-pipeline', ...workflowSelection.selectedValues])
2321
+ : workflowSelection.selectedValues;
2322
+ const workflowSelectedEntries = [
2323
+ ...workflowSelection.selectedEntries,
2324
+ ...selectedWorkflowValues
2325
+ .filter((value) => !workflowSelection.selectedEntries.some((entry) => entry.name === value || entry.id === value))
2326
+ .map((value) => this.toArtifactAuditEntry('workflow', value, workflowItems, {
2327
+ score: 1,
2328
+ source: 'planner',
2329
+ confidence: 'high',
2330
+ reason: 'Release intent requires the release-pipeline workflow so package, audit, and smoke evidence is collected.',
2331
+ selector: 'name',
2332
+ selectionPolicy: 'global-first',
2333
+ authorityTier: this.getAuthorityTier('workflow'),
2334
+ })),
2335
+ ];
2319
2336
  const hookSelection = this.resolveCatalogVotes('hook', task, intent, hookItems, {
2320
2337
  explicit: options.hookSelectors,
2321
2338
  planner: [],
@@ -2356,7 +2373,7 @@ export class OrchestratorEngine {
2356
2373
  selectionSummary: {
2357
2374
  specialists: specialistSelection.selectedValues.length,
2358
2375
  skills: skillSelection.selectedValues.length,
2359
- workflows: workflowSelection.selectedValues.length,
2376
+ workflows: selectedWorkflowValues.length,
2360
2377
  hooks: hookSelection.selectedValues.length,
2361
2378
  automations: automationSelection.selectedValues.length,
2362
2379
  crews: crewSelection.selectedValues.length,
@@ -2367,7 +2384,7 @@ export class OrchestratorEngine {
2367
2384
  ...crewSelection.selectedEntries,
2368
2385
  ...specialistSelection.selectedEntries,
2369
2386
  ...skillSelection.selectedEntries,
2370
- ...workflowSelection.selectedEntries,
2387
+ ...workflowSelectedEntries,
2371
2388
  ...hookSelection.selectedEntries,
2372
2389
  ...automationSelection.selectedEntries,
2373
2390
  ];
@@ -2388,7 +2405,7 @@ export class OrchestratorEngine {
2388
2405
  selectionSummary: {
2389
2406
  specialists: specialistSelection.selectedValues.length,
2390
2407
  skills: skillSelection.selectedValues.length,
2391
- workflows: workflowSelection.selectedValues.length,
2408
+ workflows: selectedWorkflowValues.length,
2392
2409
  hooks: hookSelection.selectedValues.length,
2393
2410
  automations: automationSelection.selectedValues.length,
2394
2411
  crews: crewSelection.selectedValues.length,
@@ -2400,7 +2417,7 @@ export class OrchestratorEngine {
2400
2417
  crew: crewSelection.selectedValues[0],
2401
2418
  specialists: specialistSelection.selectedValues,
2402
2419
  skills: skillSelection.selectedValues,
2403
- workflows: workflowSelection.selectedValues,
2420
+ workflows: selectedWorkflowValues,
2404
2421
  hooks: hookSelection.selectedValues,
2405
2422
  automations: automationSelection.selectedValues,
2406
2423
  audit: {
@@ -2618,6 +2635,8 @@ export class OrchestratorEngine {
2618
2635
  isCatalogVoteSelected(entry) {
2619
2636
  if (entry.eligible === false)
2620
2637
  return false;
2638
+ if (!entry.item && entry.source !== 'explicit')
2639
+ return false;
2621
2640
  if (entry.source === 'explicit' || entry.source === 'planner')
2622
2641
  return true;
2623
2642
  if (entry.source === 'runtime-resolver')
@@ -2657,6 +2676,12 @@ export class OrchestratorEngine {
2657
2676
  if (entry.source === 'explicit') {
2658
2677
  return { eligible: true };
2659
2678
  }
2679
+ if (!entry.item) {
2680
+ return {
2681
+ eligible: false,
2682
+ blockedReason: 'Recommended artifact is not present in the runtime catalog.',
2683
+ };
2684
+ }
2660
2685
  if (entry.source === 'scorer' && entry.confidence === 'low') {
2661
2686
  return {
2662
2687
  eligible: false,
@@ -568,30 +568,39 @@ export const BUILTIN_SKILL_PACKS = BASE_DOMAIN_SKILLS.flatMap((seed) => ([
568
568
  promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
569
569
  },
570
570
  ]));
571
- export const BUILTIN_WORKFLOW_PACKS = BASE_DOMAIN_SKILLS.flatMap((seed) => ([
571
+ const SPECIALIZED_WORKFLOW_PACKS = [
572
572
  {
573
- key: `${seed.domain}-execution-loop`,
574
- name: `${seed.domain}-execution-loop`,
575
- domain: seed.domain,
576
- description: `Bundled ${seed.domain} workflow with planning, execution, and verification checkpoints.`,
577
- triggerConditions: [`goal mentions ${seed.domain}`, `${seed.domain} pack selected`],
578
- expectedOutputs: seed.outputs,
579
- guardrails: seed.guardrails,
580
- verifierHooks: ['Collect verifier evidence before workflow promotion.', ...seed.verifierHooks],
581
- roleAffinity: ['planner', 'coder', 'verifier', 'skill-maker'],
573
+ key: 'release-pipeline',
574
+ name: 'release-pipeline',
575
+ domain: 'pdlc',
576
+ description: 'Release workflow with packaging, audit, smoke, and publish-readiness checkpoints.',
577
+ triggerConditions: ['release requested', 'publish or deploy task detected', 'version or tag work selected'],
578
+ expectedOutputs: ['release checklist', 'verification evidence', 'publish decision'],
579
+ guardrails: [
580
+ 'Do not treat a draft release as published.',
581
+ 'Do not publish without explicit release-gate evidence.',
582
+ ],
583
+ verifierHooks: [
584
+ 'Attach package, audit, and smoke evidence before promotion.',
585
+ 'Record publish-readiness outcome in the run ledger.',
586
+ ],
587
+ roleAffinity: ['planner', 'verifier', 'devops'],
582
588
  steps: [
583
589
  {
584
- title: `Plan ${seed.domain} outcome and artifacts`,
590
+ title: 'Frame release criteria and affected package surface',
585
591
  checkpoint: 'before-read',
586
592
  role: 'planner',
587
593
  },
588
594
  {
589
- title: `Execute ${seed.domain} changes with bounded scope`,
590
- checkpoint: 'before-mutate',
591
- role: 'coder',
595
+ title: 'Collect package, audit, and smoke evidence',
596
+ checkpoint: 'before-verify',
597
+ role: 'verifier',
598
+ bindings: [
599
+ { type: 'run_command', command: 'npm run qa:release' },
600
+ ],
592
601
  },
593
602
  {
594
- title: `Review ${seed.domain} outputs and attach evidence`,
603
+ title: 'Record publish readiness and remaining blockers',
595
604
  checkpoint: 'before-verify',
596
605
  role: 'verifier',
597
606
  },
@@ -599,35 +608,104 @@ export const BUILTIN_WORKFLOW_PACKS = BASE_DOMAIN_SKILLS.flatMap((seed) => ([
599
608
  promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
600
609
  },
601
610
  {
602
- key: `${seed.domain}-approval-loop`,
603
- name: `${seed.domain}-approval-loop`,
604
- domain: seed.domain,
605
- description: `Bundled ${seed.domain} approval loop with review, verification, and promotion control.`,
606
- triggerConditions: [`${seed.domain} approval requested`, `${seed.domain} promotion under review`],
607
- expectedOutputs: ['approval note', 'review evidence', 'promotion decision'],
608
- guardrails: ['Do not approve unverified work.', ...seed.guardrails],
609
- verifierHooks: ['Require approval evidence before promotion.', ...seed.verifierHooks],
610
- roleAffinity: ['planner', 'verifier', 'research-shadow'],
611
+ key: 'research-and-implement',
612
+ name: 'research-and-implement',
613
+ domain: 'deep-tech',
614
+ description: 'Research-backed implementation workflow tying retrieved context to bounded code changes and verification.',
615
+ triggerConditions: ['rag context attached', 'research-backed implementation requested', 'unknown code path requires grounding'],
616
+ expectedOutputs: ['source-grounded plan', 'bounded implementation notes', 'verification evidence'],
617
+ guardrails: [
618
+ 'Do not promote research claims without source-grounded evidence.',
619
+ 'Keep implementation scope tied to retrieved context.',
620
+ ],
621
+ verifierHooks: [
622
+ 'Attach source-grounding notes before verification.',
623
+ 'Record how research context changed the implementation decision.',
624
+ ],
625
+ roleAffinity: ['planner', 'coder', 'verifier', 'research-shadow'],
611
626
  steps: [
612
627
  {
613
- title: `Frame ${seed.domain} approval criteria`,
628
+ title: 'Extract source-grounded implementation constraints',
614
629
  checkpoint: 'before-read',
615
630
  role: 'planner',
616
631
  },
617
632
  {
618
- title: `Review ${seed.domain} evidence and risks`,
619
- checkpoint: 'before-verify',
620
- role: 'verifier',
633
+ title: 'Apply the smallest implementation compatible with the research context',
634
+ checkpoint: 'before-mutate',
635
+ role: 'coder',
621
636
  },
622
637
  {
623
- title: `Publish ${seed.domain} approval outcome`,
624
- checkpoint: 'retry',
625
- role: 'research-shadow',
638
+ title: 'Verify behavior and cite grounding evidence',
639
+ checkpoint: 'before-verify',
640
+ role: 'verifier',
626
641
  },
627
642
  ],
628
643
  promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
629
644
  },
630
- ]));
645
+ ];
646
+ export const BUILTIN_WORKFLOW_PACKS = [
647
+ ...BASE_DOMAIN_SKILLS.flatMap((seed) => ([
648
+ {
649
+ key: `${seed.domain}-execution-loop`,
650
+ name: `${seed.domain}-execution-loop`,
651
+ domain: seed.domain,
652
+ description: `Bundled ${seed.domain} workflow with planning, execution, and verification checkpoints.`,
653
+ triggerConditions: [`goal mentions ${seed.domain}`, `${seed.domain} pack selected`],
654
+ expectedOutputs: seed.outputs,
655
+ guardrails: seed.guardrails,
656
+ verifierHooks: ['Collect verifier evidence before workflow promotion.', ...seed.verifierHooks],
657
+ roleAffinity: ['planner', 'coder', 'verifier', 'skill-maker'],
658
+ steps: [
659
+ {
660
+ title: `Plan ${seed.domain} outcome and artifacts`,
661
+ checkpoint: 'before-read',
662
+ role: 'planner',
663
+ },
664
+ {
665
+ title: `Execute ${seed.domain} changes with bounded scope`,
666
+ checkpoint: 'before-mutate',
667
+ role: 'coder',
668
+ },
669
+ {
670
+ title: `Review ${seed.domain} outputs and attach evidence`,
671
+ checkpoint: 'before-verify',
672
+ role: 'verifier',
673
+ },
674
+ ],
675
+ promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
676
+ },
677
+ {
678
+ key: `${seed.domain}-approval-loop`,
679
+ name: `${seed.domain}-approval-loop`,
680
+ domain: seed.domain,
681
+ description: `Bundled ${seed.domain} approval loop with review, verification, and promotion control.`,
682
+ triggerConditions: [`${seed.domain} approval requested`, `${seed.domain} promotion under review`],
683
+ expectedOutputs: ['approval note', 'review evidence', 'promotion decision'],
684
+ guardrails: ['Do not approve unverified work.', ...seed.guardrails],
685
+ verifierHooks: ['Require approval evidence before promotion.', ...seed.verifierHooks],
686
+ roleAffinity: ['planner', 'verifier', 'research-shadow'],
687
+ steps: [
688
+ {
689
+ title: `Frame ${seed.domain} approval criteria`,
690
+ checkpoint: 'before-read',
691
+ role: 'planner',
692
+ },
693
+ {
694
+ title: `Review ${seed.domain} evidence and risks`,
695
+ checkpoint: 'before-verify',
696
+ role: 'verifier',
697
+ },
698
+ {
699
+ title: `Publish ${seed.domain} approval outcome`,
700
+ checkpoint: 'retry',
701
+ role: 'research-shadow',
702
+ },
703
+ ],
704
+ promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
705
+ },
706
+ ])),
707
+ ...SPECIALIZED_WORKFLOW_PACKS,
708
+ ];
631
709
  export function slugify(value) {
632
710
  return value
633
711
  .toLowerCase()
@@ -40,7 +40,7 @@ export interface SessionSummary {
40
40
  sessionId: string;
41
41
  timestamp: number;
42
42
  files: string[];
43
- fileSHAs: Record<string, string>;
43
+ fileSHAs?: Record<string, string>;
44
44
  summary: string;
45
45
  keyDecisions: string[];
46
46
  }
@@ -204,6 +204,10 @@ export class TokenSupremacyEngine {
204
204
  // Saves ~60-70% tokens on follow-up sessions
205
205
  // ───────────────────────────────────────────────────────────────────────────
206
206
  differential(prev, curr) {
207
+ const previousFileSHAs = prev.fileSHAs && typeof prev.fileSHAs === 'object'
208
+ ? prev.fileSHAs
209
+ : {};
210
+ const hasComparableFingerprints = Object.keys(previousFileSHAs).length > 0;
207
211
  const delta = {
208
212
  added: [],
209
213
  changed: [],
@@ -214,10 +218,15 @@ export class TokenSupremacyEngine {
214
218
  : undefined,
215
219
  };
216
220
  for (const file of curr) {
217
- const prevSHA = prev.fileSHAs[file.path];
221
+ const prevSHA = previousFileSHAs[file.path];
218
222
  const currSHA = this.fingerprintFile(file);
219
223
  if (!prevSHA) {
220
- delta.added.push(file);
224
+ if (hasComparableFingerprints) {
225
+ delta.added.push(file);
226
+ }
227
+ else {
228
+ delta.changed.push(file);
229
+ }
221
230
  }
222
231
  else if (prevSHA !== currSHA) {
223
232
  delta.changed.push(file);
@@ -8,6 +8,10 @@ import { type WorktreeHealthSnapshot } from '../../engines/worktree-health.js';
8
8
  import type { MergeDecision } from '../index.js';
9
9
  import type { RuntimeWorkerResult, WorkerRole } from '../runtime.js';
10
10
  import { type CommandRecord } from './worker.js';
11
+ export interface BindingApplicationResult {
12
+ modifiedFiles: string[];
13
+ commandRecords: CommandRecord[];
14
+ }
11
15
  /**
12
16
  * Lightweight per-run artifact writer. Maintains a flat index of paths
13
17
  * written so callers can recover the full run artifact layout.
@@ -36,11 +40,12 @@ export declare class WorktreeSession {
36
40
  constructor(repoRoot: string, workerId: string, role: WorkerRole, recorder: ArtifactRecorder, reportHealth?: (snapshot: WorktreeHealthSnapshot) => void);
37
41
  create(): Promise<void>;
38
42
  run(command: string, allowFailure?: boolean, timeoutMs?: number): Promise<CommandRecord>;
39
- applyBindings(bindings: SkillBinding[], timeoutMs?: number): Promise<string[]>;
43
+ applyBindings(bindings: SkillBinding[], timeoutMs?: number): Promise<BindingApplicationResult>;
40
44
  captureDiff(): Promise<string>;
41
45
  applyPatchContent(diff: string): Promise<void>;
42
46
  cleanup(): Promise<void>;
43
47
  private resolve;
48
+ private linkWorkspaceDependencies;
44
49
  }
45
50
  /** Thin adapter that wraps a MemoryBackend for use inside MergeOracle. */
46
51
  export declare class MergeMemoryAdapter {
@@ -33,14 +33,14 @@ export class ArtifactRecorder {
33
33
  writeJson(relativePath, value) {
34
34
  const target = path.join(this.runDir, relativePath);
35
35
  fs.mkdirSync(path.dirname(target), { recursive: true });
36
- void fs.promises.writeFile(target, JSON.stringify(value, null, 2), 'utf-8');
36
+ fs.writeFileSync(target, JSON.stringify(value, null, 2), 'utf-8');
37
37
  this.index[relativePath] = target;
38
38
  return target;
39
39
  }
40
40
  writeText(relativePath, value) {
41
41
  const target = path.join(this.runDir, relativePath);
42
42
  fs.mkdirSync(path.dirname(target), { recursive: true });
43
- void fs.promises.writeFile(target, value, 'utf-8');
43
+ fs.writeFileSync(target, value, 'utf-8');
44
44
  this.index[relativePath] = target;
45
45
  return target;
46
46
  }
@@ -75,6 +75,7 @@ export class WorktreeSession {
75
75
  cwd: this.repoRoot,
76
76
  maxBuffer: 1024 * 1024 * 20,
77
77
  });
78
+ await this.linkWorkspaceDependencies();
78
79
  }
79
80
  catch (error) {
80
81
  const retryHealth = await doctorGitWorktrees(this.repoRoot);
@@ -87,6 +88,7 @@ export class WorktreeSession {
87
88
  }
88
89
  async applyBindings(bindings, timeoutMs) {
89
90
  const modified = new Set();
91
+ const commandRecords = [];
90
92
  for (const binding of bindings) {
91
93
  switch (binding.type) {
92
94
  case 'write_file': {
@@ -114,12 +116,13 @@ export class WorktreeSession {
114
116
  }
115
117
  case 'run_command': {
116
118
  const record = await this.run(binding.command || '', false, timeoutMs);
119
+ commandRecords.push(record);
117
120
  this.recorder.writeJson(path.join('workers', this.workerId, `${sanitizeFileName(record.command)}.json`), record);
118
121
  break;
119
122
  }
120
123
  }
121
124
  }
122
- return [...modified];
125
+ return { modifiedFiles: [...modified], commandRecords };
123
126
  }
124
127
  async captureDiff() {
125
128
  try {
@@ -130,6 +133,12 @@ export class WorktreeSession {
130
133
  catch {
131
134
  // Runtime skill overlays stay outside repo patches.
132
135
  }
136
+ try {
137
+ await exec('git reset HEAD -- node_modules', { cwd: this.worktreeDir, maxBuffer: 1024 * 1024 * 20 });
138
+ }
139
+ catch {
140
+ // Shared dependency symlink stays outside repo patches.
141
+ }
133
142
  const { stdout } = await exec('git diff --binary --cached HEAD', {
134
143
  cwd: this.worktreeDir,
135
144
  maxBuffer: 1024 * 1024 * 20,
@@ -167,6 +176,23 @@ export class WorktreeSession {
167
176
  return target;
168
177
  return path.join(this.worktreeDir, target);
169
178
  }
179
+ async linkWorkspaceDependencies() {
180
+ const source = path.join(this.repoRoot, 'node_modules');
181
+ const target = path.join(this.worktreeDir, 'node_modules');
182
+ try {
183
+ const stat = await fs.promises.lstat(source);
184
+ if (!stat.isDirectory() && !stat.isSymbolicLink())
185
+ return;
186
+ await fs.promises.lstat(target).then(() => undefined).catch(async (error) => {
187
+ if (error?.code !== 'ENOENT')
188
+ throw error;
189
+ await fs.promises.symlink(source, target, 'dir');
190
+ });
191
+ }
192
+ catch {
193
+ // Worktrees remain valid for repos that do not use node_modules or have not installed deps.
194
+ }
195
+ }
170
196
  }
171
197
  // ─── MergeMemoryAdapter ───────────────────────────────────────────────────────
172
198
  /** Thin adapter that wraps a MemoryBackend for use inside MergeOracle. */
@@ -187,6 +187,8 @@ export interface WorkerVerification {
187
187
  workerId: string;
188
188
  verifierId: string;
189
189
  passed: boolean;
190
+ status: 'passed' | 'failed' | 'skipped';
191
+ skippedReason?: string;
190
192
  commands: CommandRecord[];
191
193
  summary: string;
192
194
  artifactsPath: string;
@@ -197,6 +199,7 @@ export interface RuntimeWorkerResult extends WorkerResult {
197
199
  verification?: WorkerVerification;
198
200
  artifactsPath: string;
199
201
  modifiedFiles: string[];
202
+ commandRecords?: CommandRecord[];
200
203
  }
201
204
  export interface PlannerResult {
202
205
  summary: string;
@@ -531,6 +534,7 @@ export declare class SubAgentRuntime {
531
534
  private applyDecision;
532
535
  private evaluatePromotions;
533
536
  private resolveCandidateDiff;
537
+ private isCommandOnlyOperationalSuccess;
534
538
  private resolveFileRefs;
535
539
  private discoverTargetFiles;
536
540
  private collectLibraryCounts;
@@ -619,6 +619,7 @@ export class SubAgentRuntime {
619
619
  workerId: manifest.targetWorkerId ?? manifest.workerId,
620
620
  verifierId: manifest.workerId,
621
621
  passed: false,
622
+ status: 'failed',
622
623
  commands: [],
623
624
  summary: target?.budgetExceeded
624
625
  ? 'Skipped verification for over-budget worker.'
@@ -637,12 +638,14 @@ export class SubAgentRuntime {
637
638
  }
638
639
  const verification = verificationResults.find((entry) => entry.workerId === result.workerId);
639
640
  if (verification) {
641
+ const verificationPassed = verification.status === 'passed' || (verification.passed && verification.status !== 'skipped');
640
642
  result.verification = verification;
641
- result.verified = verification.passed;
643
+ result.verified = verificationPassed;
642
644
  result.testsPassing = verification.commands.filter((command) => command.exitCode === 0).length;
643
- result.outcome = verification.passed ? 'success' : (result.diff.trim() ? 'partial' : 'failed');
645
+ result.outcome = verificationPassed ? 'success' : (result.diff.trim() ? 'partial' : 'failed');
644
646
  }
645
647
  }
648
+ recorder.writeJson('worker-results.json', run.workerResults);
646
649
  const decision = await consensusPolicy.merge(mergeableCoderResults);
647
650
  run.finalDecision = decision;
648
651
  run.backendEvidence.consensus = consensusPolicy.shadowStats();
@@ -685,14 +688,15 @@ export class SubAgentRuntime {
685
688
  : enforcedBlockingGate
686
689
  ? { applied: false, rolledBack: false, summary: `Review gate ${enforcedBlockingGate.gate} remains ${enforcedBlockingGate.status}.` }
687
690
  : await this.applyDecision(recorder, { ...task, verifyCommands: beforeVerifyResolved.verifyCommands }, decision, consensusPolicy);
688
- // Read-only intents (inspect/plan) don't produce a diff to apply, so applied=false
689
- // is the expected outcome not a failure. Only mark 'failed' when a shield blocked,
690
- // a review gate enforced, or a mutate run produced no applicable diff.
691
+ const commandOnlyOperationalSuccess = this.isCommandOnlyOperationalSuccess(task, decision);
692
+ // Read-only and command-only operational runs don't produce a diff to apply.
693
+ // Only mark 'failed' when a shield blocked, a review gate enforced, or a
694
+ // mutate run produced neither a patch nor successful command evidence.
691
695
  const readOnlyIntent = task.intent === 'inspect' || task.intent === 'plan';
692
696
  const failureExpected = preApplyShield.blocked || Boolean(enforcedBlockingGate);
693
697
  run.state = applied.applied
694
698
  ? (applied.rolledBack ? 'rolled_back' : 'merged')
695
- : (readOnlyIntent && !failureExpected ? 'inspected' : 'failed');
699
+ : ((readOnlyIntent || commandOnlyOperationalSuccess) && !failureExpected ? 'inspected' : 'failed');
696
700
  // Surface completion on the SSE feed so the dashboard can move the
697
701
  // run from "Running" to "Done" without a poll round-trip.
698
702
  try {
@@ -2045,13 +2049,20 @@ export class SubAgentRuntime {
2045
2049
  verified: false,
2046
2050
  artifactsPath: workerDir,
2047
2051
  modifiedFiles: [],
2052
+ commandRecords: [],
2048
2053
  };
2049
2054
  }
2050
- const modifiedFiles = await session.applyBindings(allowedActions, timeoutMs);
2055
+ const bindingResult = await session.applyBindings(allowedActions, timeoutMs);
2056
+ const modifiedFiles = bindingResult.modifiedFiles;
2057
+ const commandRecords = bindingResult.commandRecords;
2051
2058
  modifiedFiles.forEach(file => {
2052
2059
  this.sessionDNA?.recordFileModified(file);
2053
2060
  learnings.push(`Modified ${file}`);
2054
2061
  });
2062
+ if (commandRecords.length > 0) {
2063
+ const passedCommands = commandRecords.filter((record) => record.exitCode === 0).length;
2064
+ learnings.push(`Ran ${commandRecords.length} command binding(s), ${passedCommands} passed.`);
2065
+ }
2055
2066
  const diff = await session.captureDiff();
2056
2067
  recorder.writeText(path.join('workers', manifest.workerId, 'diff.patch'), diff);
2057
2068
  podNetwork.publish(manifest.workerId, `Produced ${modifiedFiles.length} modified files`, 0.8, ['#runtime-worker']);
@@ -2060,23 +2071,30 @@ export class SubAgentRuntime {
2060
2071
  if (budgetExceeded) {
2061
2072
  learnings.push(`Token budget exceeded: used ${estimatedTokens}, budget ${manifest.tokenBudget}`);
2062
2073
  }
2063
- nexusEventBus.emit('phantom.worker.complete', { workerId: manifest.workerId, confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.7 : 0.2) });
2074
+ const commandOnlySuccess = !diff.trim()
2075
+ && commandRecords.length > 0
2076
+ && commandRecords.every((record) => record.exitCode === 0);
2077
+ nexusEventBus.emit('phantom.worker.complete', {
2078
+ workerId: manifest.workerId,
2079
+ confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.7 : (commandOnlySuccess ? 0.75 : 0.2)),
2080
+ });
2064
2081
  return {
2065
2082
  workerId: manifest.workerId,
2066
2083
  role: manifest.role,
2067
2084
  taskId: runId,
2068
2085
  approach: manifest.strategy,
2069
2086
  diff,
2070
- outcome: budgetExceeded ? 'budgetExceeded' : (diff.trim() ? 'partial' : 'failed'),
2071
- confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.68 : 0.18),
2087
+ outcome: budgetExceeded ? 'budgetExceeded' : (diff.trim() ? 'partial' : (commandOnlySuccess ? 'success' : 'failed')),
2088
+ confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.68 : (commandOnlySuccess ? 0.82 : 0.18)),
2072
2089
  tokensUsed: estimatedTokens,
2073
2090
  tokenEstimateSource: 'runtime-estimate',
2074
2091
  budgetExceeded,
2075
2092
  learnings,
2076
- testsPassing: 0,
2093
+ testsPassing: commandRecords.filter((record) => record.exitCode === 0).length,
2077
2094
  verified: false,
2078
2095
  artifactsPath: workerDir,
2079
2096
  modifiedFiles,
2097
+ commandRecords,
2080
2098
  };
2081
2099
  }
2082
2100
  catch (error) {
@@ -2099,6 +2117,7 @@ export class SubAgentRuntime {
2099
2117
  verified: false,
2100
2118
  artifactsPath: workerDir,
2101
2119
  modifiedFiles: [],
2120
+ commandRecords: [],
2102
2121
  };
2103
2122
  }
2104
2123
  finally {
@@ -2130,15 +2149,31 @@ export class SubAgentRuntime {
2130
2149
  const verifier = new WorktreeSession(this.repoRoot, `${runId}-${manifest.workerId}`, 'verifier', recorder, (snapshot) => this.recordWorktreeHealth(snapshot));
2131
2150
  const records = [];
2132
2151
  const artifactsPath = recorder.workerDir(manifest.workerId);
2133
- // Skip worktree creation if there is nothing to verify. An empty diff
2134
- // is not a verification failure — it means the worker produced no
2135
- // mutation (typical for inspect/plan intents). Mark passed=true so
2136
- // the run state is not flagged as failed for read-only orchestrations.
2152
+ const commandRecords = target?.commandRecords ?? [];
2153
+ const commandOnlySuccess = !target?.diff?.trim()
2154
+ && commandRecords.length > 0
2155
+ && commandRecords.every((record) => record.exitCode === 0);
2156
+ // Skip worktree creation if there is no diff. Command-only operational
2157
+ // workers are verified from their command records; pure no-op workers
2158
+ // remain skipped rather than counted as implementation evidence.
2137
2159
  if (!target?.diff?.trim()) {
2160
+ if (commandOnlySuccess) {
2161
+ return {
2162
+ workerId: manifest.targetWorkerId ?? 'unknown',
2163
+ verifierId: manifest.workerId,
2164
+ passed: true,
2165
+ status: 'passed',
2166
+ commands: commandRecords,
2167
+ summary: `Verifier accepted command-only evidence (${commandRecords.length} command binding(s) passed).`,
2168
+ artifactsPath,
2169
+ };
2170
+ }
2138
2171
  return {
2139
2172
  workerId: manifest.targetWorkerId ?? 'unknown',
2140
2173
  verifierId: manifest.workerId,
2141
- passed: true,
2174
+ passed: false,
2175
+ status: 'skipped',
2176
+ skippedReason: 'no_candidate_diff',
2142
2177
  commands: [],
2143
2178
  summary: 'Verifier skipped — no candidate diff to verify (read-only or no-op run).',
2144
2179
  artifactsPath,
@@ -2175,6 +2210,7 @@ export class SubAgentRuntime {
2175
2210
  workerId: manifest.targetWorkerId ?? 'unknown',
2176
2211
  verifierId: manifest.workerId,
2177
2212
  passed,
2213
+ status: passed ? 'passed' : 'failed',
2178
2214
  commands: records,
2179
2215
  summary: passed
2180
2216
  ? `Verifier passed ${records.length} command(s).`
@@ -2190,6 +2226,7 @@ export class SubAgentRuntime {
2190
2226
  workerId: manifest.targetWorkerId ?? 'unknown',
2191
2227
  verifierId: manifest.workerId,
2192
2228
  passed: false,
2229
+ status: 'failed',
2193
2230
  commands: [],
2194
2231
  summary,
2195
2232
  artifactsPath,
@@ -2202,6 +2239,14 @@ export class SubAgentRuntime {
2202
2239
  async applyDecision(recorder, task, decision, consensusPolicy) {
2203
2240
  const candidateDiff = this.resolveCandidateDiff(decision);
2204
2241
  if (!candidateDiff.trim()) {
2242
+ if (this.isCommandOnlyOperationalSuccess(task, decision)) {
2243
+ const records = decision.winner?.commandRecords ?? [];
2244
+ return {
2245
+ applied: false,
2246
+ rolledBack: false,
2247
+ summary: `Operational workflow completed with command evidence only (${records.length} command binding(s) passed); no repository patch was required.`,
2248
+ };
2249
+ }
2205
2250
  return {
2206
2251
  applied: false,
2207
2252
  rolledBack: false,
@@ -2402,6 +2447,20 @@ export class SubAgentRuntime {
2402
2447
  }
2403
2448
  return decision.winner?.diff ?? '';
2404
2449
  }
2450
+ isCommandOnlyOperationalSuccess(task, decision) {
2451
+ if (decision.action === 'reject')
2452
+ return false;
2453
+ if (this.resolveCandidateDiff(decision).trim())
2454
+ return false;
2455
+ const winner = decision.winner;
2456
+ const records = winner?.commandRecords ?? [];
2457
+ if (records.length === 0 || !records.every((record) => record.exitCode === 0)) {
2458
+ return false;
2459
+ }
2460
+ return task.releasePolicy.mode === 'ship-ready'
2461
+ || task.workflowSelectors.some((selector) => /release|deploy|publish|ci|smoke|audit/i.test(selector))
2462
+ || /release|publish|deploy|npm|github action|ci\/cd|smoke|audit/i.test(task.goal);
2463
+ }
2405
2464
  async resolveFileRefs(files) {
2406
2465
  return Promise.all(files.map(async (file) => {
2407
2466
  const resolved = path.isAbsolute(file) ? file : path.join(this.repoRoot, file);
@@ -2945,8 +3004,8 @@ export class SubAgentRuntime {
2945
3004
  };
2946
3005
  }
2947
3006
  evaluateReviewGates(run) {
2948
- const verificationPassed = run.verificationResults.filter((result) => result.passed).length;
2949
- const verificationFailed = run.verificationResults.filter((result) => !result.passed).length;
3007
+ const verificationPassed = run.verificationResults.filter((result) => result.status === 'passed' || (result.passed && result.status !== 'skipped')).length;
3008
+ const verificationFailed = run.verificationResults.filter((result) => result.status === 'failed' || (!result.passed && result.status !== 'skipped')).length;
2950
3009
  const hasPlannerEvidence = Boolean(run.plannerState?.ledger?.length || run.plannerResult?.summary || run.executionLedger?.steps?.some((step) => step.id === 'planner-selection' && step.status === 'completed'));
2951
3010
  const hasImplementation = run.workerResults.some((result) => result.role === 'coder');
2952
3011
  const hasVerifiedImplementation = run.workerResults.some((result) => result.role === 'coder' && result.verified);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-prime",
3
- "version": "7.9.6",
3
+ "version": "7.9.8",
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",