# How Features Self-Register and Are Provided in agent-core-v2: A Complete Guide

> Discover how agent-core-v2 features self-register automatically and are provisioned through a declarative three-phase lifecycle. Learn the complete guide here.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: how-to-guide
- Published: 2026-08-13

---

**Features in agent-core-v2 automatically register themselves via a global registry and are provisioned into dependency injection scopes through a declarative three-phase lifecycle.**

In the MoonshotAI/kimi-code repository, the `agent-core-v2` package implements a modular plugin architecture where **Features** serve as first-class units that contribute services, configuration, and runtime capabilities. Understanding how these Features self-register and integrate into specific scopes—such as App, Workspace, Session, or Agent—is essential for extending the agent engine without manual wiring.

## The Three-Phase Feature Lifecycle

Every Feature in agent-core-v2 follows a strict lifecycle that separates declaration from registration and provision. This design enables a fully decoupled, self-contained plugin model.

### Phase 1: Declaration via the Feature Base Class

A Feature begins as a class extending the abstract base `Feature` in [`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts). Within its constructor, developers declare contributions using helper methods like `contributeService`, `contributeConfig`, or `contributeProvider`. These declarations specify what the Feature will supply to the dependency injection container without immediately instantiating or binding the services.

### Phase 2: Self-Registration with registerFeature

Self-registration occurs through the exported `registerFeature` function defined in [`src/features/featureAssembly.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssembly.ts). When a module containing a concrete Feature is evaluated, calling `registerFeature(MyFeature)` stores the constructor in a singleton `FeatureRegistry`.

```typescript
// src/features/featureAssembly.ts
export function registerFeature<T extends Feature>(ctor: new (...args: any[]) => T) {
    FeatureRegistry.instance.register(ctor);
}

```

The registry maintains an array of Feature constructors. This global registration mechanism means that merely importing a Feature module and invoking `registerFeature` makes the Feature discoverable throughout the system without additional configuration.

### Phase 3: Provision via FeatureManagerService

When a scope (App, Workspace, Session, or Agent) initializes, the `FeatureManagerService` iterates over registered constructors and provisions them. The `provideUnit` method in [`src/app/feature/featureManagerService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/app/feature/featureManagerService.ts) instantiates each Feature and triggers its contribution to the target scope.

```typescript
// src/app/feature/featureManagerService.ts
async provideUnit<T extends Feature>(FeatureCtor: new (...args: any[]) => T) {
    const feature = this.instantiationService.createInstance(FeatureCtor);
    feature.provide(this.scope);
}

```

The Feature's `provide` method receives the target scope and executes the previously declared `contribute*` helpers, which internally call the DI container's binding APIs.

## Understanding the Global FeatureRegistry

The `FeatureRegistry` acts as a centralized singleton that decouples Feature discovery from Feature consumption. When `registerFeature` is called, the registry stores the constructor in an internal array. This pattern allows the framework to maintain a complete catalog of available capabilities without requiring explicit imports or manual lists in application startup code.

## Providing Services to Scopes

Scopes in agent-core-v2 represent hierarchical lifecycle boundaries. When `FeatureManagerService` provisions a Feature, it passes the specific scope instance to the `provide` method. The Feature then binds its declared services to that scope using scope units:

```typescript
this.provide(ScopeUnits(LifecycleScope.Workspace), MyService);

```

This mechanism ensures that services are instantiated and cached according to their intended lifecycle—Workspace-scoped services survive across sessions, while Session-scoped services are recreated for each new interaction.

## Practical Implementation Example

The following example demonstrates the complete flow from Feature definition to registration:

```typescript
// 1️⃣ Define a Feature that contributes a workspace-level service
import { Feature } from '#/features/feature';
import { WorkspaceService } from '#/services/workspaceService';

export class MyWorkspaceFeature extends Feature {
  constructor() {
    super();
    // Declare provision of a service bound to the Workspace scope
    this.provide(ScopeUnits(LifecycleScope.Workspace), WorkspaceService);
  }
}

// 2️⃣ Register the Feature (typically done once at application startup)
import { registerFeature } from '#/features/featureAssembly';
registerFeature(MyWorkspaceFeature);

// 3️⃣ Runtime provisioning happens automatically
// FeatureManagerService iterates the registry and calls provideUnit
// No additional wiring code required

```

## Testing Features with the Registry

The agent-core-v2 test utilities expose `_clearFeatureRecipesForTests` to reset the global registry between test cases. This isolation prevents test pollution and ensures each test starts with a clean Feature set:

```typescript
import { registerFeature, _clearFeatureRecipesForTests } from '#/features/featureAssembly';
import { SessionInitFeature } from '#/features/sessionInit/sessionInitFeature';

beforeEach(() => {
  _clearFeatureRecipesForTests();          // Reset global registry
  registerFeature(SessionInitFeature);      // Register only required Features
});

```

## Key Source Files and Their Roles

- **[`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts)** – Defines the abstract `Feature` base class and contribution helper methods.
- **[`src/features/featureAssembly.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssembly.ts)** – Implements the global `FeatureRegistry` and the `registerFeature` entry point.
- **[`src/app/feature/featureManagerService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/app/feature/featureManagerService.ts)** – Orchestrates Feature instantiation and calls `provide` on each registered Feature.
- **[`src/app/feature/featureManager.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/app/feature/featureManager.ts)** – Provides the DI-aware interface used by other system components to request Feature services.
- **[`test/features/sessionInit/sessionInitFeature.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/test/features/sessionInit/sessionInitFeature.test.ts)** – Contains examples of registry cleanup and Feature registration in unit tests.

## Summary

- Features in agent-core-v2 extend an abstract base class to declare services and configuration contributions.
- The `registerFeature` function in [`src/features/featureAssembly.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssembly.ts) adds Features to a global singleton registry, enabling self-registration.
- `FeatureManagerService.provideUnit` instantiates registered Features and binds them to specific scopes during application initialization.
- The registry can be cleared using `_clearFeatureRecipesForTests` to support isolated unit testing.
- This architecture provides a declarative, decoupled plugin model for extending agent capabilities without manual DI wiring.

## Frequently Asked Questions

### What is the difference between Feature declaration and registration in agent-core-v2?

**Declaration** occurs inside the Feature class constructor when calling `contributeService` or `provide` methods to specify what the Feature will offer. **Registration** is the external act of calling `registerFeature(MyFeature)` to add the class constructor to the global `FeatureRegistry`, making it discoverable by the system.

### How does FeatureManagerService handle Feature instantiation?

The `FeatureManagerService` uses its internal `instantiationService` to create instances of registered Features. Specifically, `provideUnit` calls `this.instantiationService.createInstance(FeatureCtor)` to instantiate the Feature before invoking its `provide` method to bind services to the target scope.

### Can Features be registered dynamically at runtime?

Yes, Features can be registered at any point before the scope assembly phase by calling `registerFeature`. However, once `FeatureManagerService` has iterated the registry and provisioned Features for a specific scope, new registrations will only take effect during subsequent scope initializations.

### How do you reset the Feature registry during unit testing?

Import `_clearFeatureRecipesForTests` from [`src/features/featureAssembly.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssembly.ts) and call it in a `beforeEach` block. This clears the singleton `FeatureRegistry` instance, allowing tests to register only the specific Features required for the test case without interference from previously registered Features.