fix(server): realtime handler (#15146)

#### PR Dependency Tree


* **PR #15146** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Reworked real-time backend wiring to centralize workspace, comments,
and Copilot embedding handlers under a unified server setup.
* Updated Copilot embedding real-time handling to use context
configuration when publishing updates.
* **New Features**
* Added automatic startup validation to ensure all required real-time
request/topic handlers are registered (for applicable server flavors).
* **Bug Fixes**
* Workspace real-time access now determines team status from quota
state.
* Improved Copilot embedding progress publishing (including completion
events).
* **Tests**
* Expanded real-time registry completeness and Copilot embedding
provider coverage.
  * Added quota-state restoration coverage after clearing stale expiry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-06-24 15:18:35 +08:00
committed by GitHub
parent c1c19be271
commit c41d613b6e
18 changed files with 425 additions and 38 deletions
@@ -10,7 +10,6 @@ import {
registerRealtimeLiveQuery,
} from '../../../core/realtime';
import { Models } from '../../../models';
import { CopilotContextService } from './service';
export function workspaceEmbeddingRoom(workspaceId: string) {
return realtimeWorkspaceEmbeddingProgressRoom(workspaceId);
@@ -21,7 +20,6 @@ export class CopilotEmbeddingRealtimeProvider implements OnModuleInit {
constructor(
private readonly ac: PermissionAccess,
private readonly models: Models,
private readonly context: CopilotContextService,
private readonly registry: RealtimeRegistry,
private readonly publisher: RealtimePublisher
) {}
@@ -35,7 +33,9 @@ export class CopilotEmbeddingRealtimeProvider implements OnModuleInit {
input,
handle: async (user, payload) => {
await this.assertCopilot(user.id, payload.workspaceId);
if (!this.context.canEmbedding) {
const canEmbedding =
await this.models.copilotWorkspace.checkEmbeddingAvailable();
if (!canEmbedding) {
return { total: 0, embedded: 0 };
}
return await this.models.copilotWorkspace.getEmbeddingStatus(
@@ -89,7 +89,8 @@ export class CopilotEmbeddingRealtimeProvider implements OnModuleInit {
reason: 'finished' | 'failed'
) {
if (!this.publisher) return;
const context = await this.context.get(contextId);
const context = await this.models.copilotContext.getConfig(contextId);
if (!context) return;
this.publishWorkspace(context.workspaceId, reason);
}
@@ -14,6 +14,7 @@ import { CopilotController } from './controller';
import { WorkspaceMcpController } from './mcp/controller';
import {
COPILOT_API_PROVIDERS,
COPILOT_CONTEXT_REALTIME_PROVIDERS,
COPILOT_FEATURE_PROVIDERS,
COPILOT_KERNEL_PROVIDERS,
COPILOT_TRANSCRIPT_REALTIME_PROVIDERS,
@@ -43,6 +44,12 @@ export class CopilotKernelModule {}
})
export class CopilotRealtimeModule {}
@Module({
imports: [PermissionModule],
providers: [...COPILOT_CONTEXT_REALTIME_PROVIDERS],
})
export class CopilotEmbeddingRealtimeModule {}
@Module({
imports: [...COPILOT_SHARED_IMPORTS, CopilotKernelModule],
providers: [...COPILOT_FEATURE_PROVIDERS],
@@ -109,9 +109,13 @@ export const COPILOT_RUNTIME_PROVIDERS = [
TurnPersistence,
];
export const COPILOT_CONTEXT_REALTIME_PROVIDERS = [
CopilotEmbeddingRealtimeProvider,
];
export const COPILOT_CONTEXT_PROVIDERS = [
CopilotContextResolver,
CopilotEmbeddingRealtimeProvider,
...COPILOT_CONTEXT_REALTIME_PROVIDERS,
];
export const COPILOT_TRANSCRIPT_REALTIME_PROVIDERS = [