How the App Scope Is Different from Other Scopes in agent-core-v2
The App Scope is the root-level, process-wide dependency injection container in agent-core-v2 that lives for the entire application lifetime, while Workspace, Session, and Agent scopes are child containers with shorter lifecycles that can inject App services but cannot be injected into them.
The agent-core-v2 package in the MoonshotAI/kimi-code repository implements a hierarchical dependency injection (DI) system that organizes services into four distinct lifetime boundaries. Understanding how the App Scope differs from its descendants is critical for correctly architecting global services like loggers, telemetry, and configuration providers that must persist across user sessions.
Understanding the Scope Hierarchy in agent-core-v2
The framework defines a strict four-level tree structure in packages/agent-core-v2/src/app/scopes.ts. Each scope represents a boundary for service instantiation and visibility.
The Four Lifecycle Scopes
| Scope | Lifetime | Typical Use | Hierarchy Position |
|---|---|---|---|
| App | Process-wide, created once at startup | Global logging, telemetry, configuration | Root (longest-lived) |
| Workspace | One per user workspace/project | File indexes, workspace-wide plugins | Child of App |
| Session | One per interactive chat session | Session metadata, transcript cache | Child of Workspace |
| Agent | One per agent instance inside a session | Per-turn data, agent-specific state | Leaf (shortest-lived) |
The enum definition in src/app/scopes.ts codifies these levels:
// packages/agent-core-v2/src/app/scopes.ts
export enum LifecycleScope {
App = 'app', // process-level, global singleton
Workspace = 'workspace',
Session = 'session',
Agent = 'agent',
}
Key Differences Between App Scope and Child Scopes
While all four scopes use the same registration API, the App Scope exhibits unique behavioral constraints that prevent circular dependencies and resource leaks.
Lifetime and Visibility Rules
App Scope services are instantiated exactly once when the process starts and remain alive until the application exits. They are visible to every lower scope in the hierarchy—Workspace, Session, and Agent services can all inject App-level dependencies. However, the reverse is strictly prohibited: an App service cannot depend on a Workspace, Session, or Agent service because those shorter-lived containers do not exist when the App scope is constructed.
This visibility rule is enforced by the DI container and documented in packages/agent-core-v2/docs/di.md: "短寿命的服务可以注入长寿命的服务,反过来不行" (Shorter-lived services can inject longer-lived services, but not vice versa).
Dependency Direction Constraints
The hierarchy forms a directed acyclic graph: App → Workspace → Session → Agent. Attempting to register an App service that depends on a Session service triggers a DI resolution error at startup. This constraint guarantees that global singletons never hold references to transient session data, preventing memory leaks and use-after-free bugs.
Registration and Activation Patterns
App-level services typically use ScopeActivation.OnScopeCreated to ensure immediate availability, while child scopes often use ScopeActivation.OnDemand for lazy instantiation. The registerScopedService function in #/_base/di/scope.ts binds a service implementation to a specific scope level:
registerScopedService(
LifecycleScope.App, // App scope
ILogService,
LogService,
ScopeActivation.OnScopeCreated, // created when App scope starts
'log',
);
Practical Implementation Examples
Registering an App-Scope Service
Global services like loggers must be registered at the App level to ensure they are available throughout the process lifetime:
import { LifecycleScope } from '#/app/scopes';
import { registerScopedService, ScopeActivation } from '#/_base/di/scope';
import { ILogService } from '#/log/log';
export class LogService implements ILogService {
declare readonly _serviceBrand: undefined;
info(msg: string) { console.log('[INFO]', msg); }
}
// App-level registration – one instance for the whole process
registerScopedService(
LifecycleScope.App,
ILogService,
LogService,
ScopeActivation.OnScopeCreated,
'log',
);
Registering a Session-Scope Service That Injects App Services
Session-level services can safely inject App-level dependencies. The following example shows a SessionMetadata service consuming the global logger:
import { LifecycleScope } from '#/app/scopes';
import { registerScopedService, ScopeActivation } from '#/_base/di/scope';
import { ISessionMetadata } from '#/session/metadata';
import { ILogService } from '#/log/log';
export class SessionMetadata implements ISessionMetadata {
declare readonly _serviceBrand: undefined;
// The logger is an App-scope service; injection is safe here.
constructor(@ILogService private readonly log: ILogService) {}
record(key: string, value: unknown) {
this.log.info(`SessionMetadata: ${key} = ${JSON.stringify(value)}`);
}
}
// Session-level registration
registerScopedService(
LifecycleScope.Session,
ISessionMetadata,
SessionMetadata,
ScopeActivation.OnDemand, // constructed on first retrieval
'sessionMetadata',
);
Illegal App-to-Session Dependencies
The following pattern demonstrates what happens when you violate the hierarchy rules. The DI container will throw an error during App scope creation because ISessionMetadata does not exist at that point:
import { LifecycleScope } from '#/app/scopes';
import { registerScopedService, ScopeActivation } from '#/_base/di/scope';
import { IAppStateService } from '#/app/state/appState';
import { ISessionMetadata } from '#/session/metadata';
export class IllegalAppService {
// ❌ Illegal: App service depends on Session-level service
constructor(@ISessionMetadata private readonly meta: ISessionMetadata) {}
}
// This registration will throw at App-scope creation time
registerScopedService(
LifecycleScope.App,
IAppStateService,
IllegalAppService,
ScopeActivation.OnScopeCreated,
'illegal',
);
Why the Scope Hierarchy Matters
The strict separation between App Scope and child scopes serves three architectural purposes:
- Resource Management – App services live for the entire process, so they must be lightweight and safe to keep resident in memory (e.g., singleton loggers or configuration caches).
- Isolation – Session and Agent scopes can be destroyed when a user ends a chat, allowing the garbage collector to reclaim memory without affecting global state.
- Dependency Safety – The tree structure guarantees a directed acyclic graph of dependencies. Attempting to create circular dependencies across scope boundaries fails fast at startup rather than causing runtime errors.
These rules are reflected in the runtime debug protocol implementation, where packages/kap-server/src/transport/channelRegistry.ts maps each LifecycleScope value to its string representation for wire protocol communication.
Summary
- The App Scope is the root DI container in agent-core-v2, created once per process and destroyed only at application exit.
- Child scopes (Workspace, Session, Agent) can inject App services, but App services cannot depend on any shorter-lived scope.
- The hierarchy
App → Workspace → Session → Agentprevents memory leaks by ensuring global singletons never reference transient session data. - Registration occurs via
registerScopedServicein#/_base/di/scope.ts, using theLifecycleScopeenum defined insrc/app/scopes.ts.
Frequently Asked Questions
What happens if an App service tries to depend on a Session service?
The DI container will throw a resolution error during App scope initialization. Because the Session scope is a child of the App scope, it does not exist when the App container is being constructed, making it impossible to satisfy the dependency. This enforcement prevents architectural errors where global singletons would hold references to transient data.
Can Workspace services access App services?
Yes. Workspace services, like all child scopes, can freely inject any service registered at the App level. This is the standard pattern for accessing global loggers, configuration stores, and telemetry providers from workspace-specific logic. The dependency direction only prohibits App services from depending on Workspace services.
How do I register a service that needs to persist across sessions?
Register the service with LifecycleScope.App in packages/agent-core-v2/src/app/scopes.ts. Use ScopeActivation.OnScopeCreated to ensure the service initializes immediately when the process starts. This pattern is used for system-wide resources like the LogService that must remain available regardless of how many individual chat sessions are active.
Where is the scope hierarchy defined in the source code?
The LifecycleScope enum is defined in packages/agent-core-v2/src/app/scopes.ts. The hierarchy rules and dependency injection guidelines are documented in packages/agent-core-v2/docs/di.md. Additionally, the debug transport layer in packages/kap-server/src/transport/channelRegistry.ts references these scope values when mapping them to protocol channel names.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →