refactor(features): extract session init feature (#2887)

- move the session init domain under features
- contribute the session service through SessionInitFeature
- cover feature withdrawal and restoration
This commit is contained in:
Haozhe 2026-08-13 18:12:08 +08:00 committed by GitHub
parent 6be26978b1
commit 4739284fb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 100 additions and 17 deletions

View file

@ -35,7 +35,7 @@ import { IProviderService } from '@moonshot-ai/agent-core-v2/kosong/provider/pro
import { ISessionApprovalService } from '@moonshot-ai/agent-core-v2/session/approval/approval';
import { ISessionInteractionService } from '@moonshot-ai/agent-core-v2/session/interaction/interaction';
import { ISessionQuestionService } from '@moonshot-ai/agent-core-v2/session/question/question';
import { ISessionInitService } from '@moonshot-ai/agent-core-v2/session/sessionInit/sessionInit';
import { ISessionInitService } from '@moonshot-ai/agent-core-v2/features/sessionInit/sessionInit';
import { ISessionMetadata } from '@moonshot-ai/agent-core-v2/session/sessionMetadata/sessionMetadata';
import { ISessionWorkspaceContext } from '@moonshot-ai/agent-core-v2/session/workspaceContext/workspaceContext';

View file

@ -0,0 +1,21 @@
import { LifecycleScope } from '#/app/scopes';
import { Feature } from '#/features/feature';
import { registerFeature } from '#/features/featureRegistry';
import { ISessionInitService } from './sessionInit';
import { SessionInitService } from './sessionInitService';
export class SessionInitFeature extends Feature {
static override readonly name = 'sessionInit';
constructor() {
super();
this.contributeService(
LifecycleScope.Session,
ISessionInitService,
SessionInitService,
);
}
}
registerFeature(SessionInitFeature);

View file

@ -22,9 +22,6 @@
* `SESSION_INIT_FAILED`) so callers can tell "aborted" from "failed".
*/
import { LifecycleScope } from '#/app/scopes';
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
import { isAbortError, isUserCancellation, userCancellationReason } from '#/_base/utils/abort';
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
import { IHostEnvironment } from '#/os/interface/hostEnvironment';
@ -144,11 +141,3 @@ export class SessionInitService implements ISessionInitService {
}
}
}
registerScopedService(
LifecycleScope.Session,
ISessionInitService,
SessionInitService,
ScopeActivation.OnScopeCreated,
'session-init',
);

View file

@ -640,9 +640,10 @@ export * from '#/agent/shellCommand/shellCommandService';
export * from '#/agent/scopeContext/scopeContext';
export * from '#/agent/stepRetry/stepRetry';
export * from '#/agent/stepRetry/stepRetryService';
export * from '#/session/sessionInit/sessionInit';
export * from '#/session/sessionInit/sessionInitService';
export * from '#/session/sessionInit/profile/init';
export * from '#/features/sessionInit/sessionInit';
export * from '#/features/sessionInit/sessionInitService';
export * from '#/features/sessionInit/profile/init';
import '#/features/sessionInit/sessionInitFeature';
export * from '#/session/todo/todoItem';
export * from '#/session/todo/todoListReminder';
export * from '#/session/todo/sessionTodo';

View file

@ -17,8 +17,8 @@ import { IWireService } from '#/wire/wire';
import { ErrorCodes, Error2 } from '#/errors';
import { IAgentLifecycleService } from '#/session/agentLifecycle/agentLifecycle';
import { ISessionContext } from '#/session/sessionContext/sessionContext';
import { ISessionInitService } from '#/session/sessionInit/sessionInit';
import { SessionInitService } from '#/session/sessionInit/sessionInitService';
import { ISessionInitService } from '#/features/sessionInit/sessionInit';
import { SessionInitService } from '#/features/sessionInit/sessionInitService';
import { ISessionSubagentService } from '#/session/subagent/subagent';
const WORK_DIR = '/project';

View file

@ -0,0 +1,72 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { ScopeActivation } from '#/_base/di/instantiation';
import {
_clearScopedRegistryForTests,
registerScopedService,
} from '#/_base/di/scope';
import { createScopedTestHost, stubPair } from '#/_base/di/test';
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
import { IFeatureManager } from '#/app/feature/featureManager';
import { FeatureManagerService } from '#/app/feature/featureManagerService';
import { LifecycleScope } from '#/app/scopes';
import { SessionInitFeature } from '#/features/sessionInit/sessionInitFeature';
import { ISessionInitService } from '#/features/sessionInit/sessionInit';
import { IHostEnvironment } from '#/os/interface/hostEnvironment';
import { IHostFileSystem } from '#/os/interface/hostFileSystem';
import { IFeatureAssemblyService } from '#/features/featureAssembly';
import { FeatureAssemblyService } from '#/features/featureAssemblyService';
import {
_clearFeatureRecipesForTests,
registerFeature,
} from '#/features/featureRegistry';
import { IAgentLifecycleService } from '#/session/agentLifecycle/agentLifecycle';
import { ISessionContext } from '#/session/sessionContext/sessionContext';
import { ISessionSubagentService } from '#/session/subagent/subagent';
describe('SessionInitFeature', () => {
beforeEach(() => {
_clearScopedRegistryForTests();
_clearFeatureRecipesForTests();
registerScopedService(
LifecycleScope.App,
IFeatureManager,
FeatureManagerService,
ScopeActivation.OnScopeCreated,
'feature',
);
registerScopedService(
LifecycleScope.App,
IFeatureAssemblyService,
FeatureAssemblyService,
ScopeActivation.OnScopeCreated,
'features',
);
registerFeature(SessionInitFeature);
});
it('withdraws and restores the Session service with the Feature', async () => {
const host = createScopedTestHost();
const session = host.child(LifecycleScope.Session, 'session-1', [
stubPair(IAgentLifecycleService, {} as IAgentLifecycleService),
stubPair(ISessionSubagentService, {} as ISessionSubagentService),
stubPair(IHostFileSystem, {} as IHostFileSystem),
stubPair(IHostEnvironment, {} as IHostEnvironment),
stubPair(IBootstrapService, {} as IBootstrapService),
stubPair(ISessionContext, {} as ISessionContext),
]);
const manager = host.app.accessor.get(IFeatureManager);
expect(session.accessor.get(ISessionInitService)).toBeDefined();
await manager.unprovideUnit('sessionInit');
await host.app.instantiation.cascade.whenIdle();
expect(() => session.accessor.get(ISessionInitService)).toThrow();
manager.provideUnit(SessionInitFeature);
await host.app.instantiation.cascade.whenIdle();
expect(session.accessor.get(ISessionInitService)).toBeDefined();
host.dispose();
});
});