Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f1415d8
feat(agents): add managed-runtime mode with Claude Platform integration
djabarovgeorge May 10, 2026
3fd2dc1
fix(agents): propagate AGENT_RUNTIME channel type across dashboard lo…
djabarovgeorge May 10, 2026
105936d
feat(agents): enhance agent creation with runtime and skills support
djabarovgeorge May 11, 2026
5b7daee
fix(agents): resolve issues with agent runtime configuration and dash…
djabarovgeorge May 11, 2026
1f9fa26
feat(agents): implement adoption of existing managed agents
djabarovgeorge May 11, 2026
953ae5b
feat(agents): enhance agent provisioning with apiKey support
djabarovgeorge May 11, 2026
7c38fe8
feat(agents): enhance agent deletion process with provider option
djabarovgeorge May 11, 2026
489d540
feat(agents): add skills support to agent runtime configuration
djabarovgeorge May 11, 2026
1dfea32
feat(agents): add creationSource field to agent DTOs and commands
djabarovgeorge May 11, 2026
3ee1d69
refactor(agents): remove runtime providers endpoint and related DTOs
djabarovgeorge May 11, 2026
4b863c8
feat(agents): enhance agent runtime configuration with skills and cap…
djabarovgeorge May 11, 2026
ea6786b
feat(agents): add externalEnvironmentId to integration schema and enh…
djabarovgeorge May 11, 2026
25168f9
Merge remote-tracking branch 'origin/next' into cursor/managed-agents…
djabarovgeorge May 11, 2026
8c502f8
fix(agents): address PR review comments
djabarovgeorge May 11, 2026
a102ad1
feat(dependencies): add @anthropic-ai/sdk and standardwebhooks to pnp…
djabarovgeorge May 11, 2026
a0a5488
Merge branch 'next' into cursor/managed-agents-claude-platform
LetItRock May 12, 2026
134c793
feat(agents): refactor managed runtime integration handling
djabarovgeorge May 12, 2026
8b6d628
Merge branch 'cursor/managed-agents-claude-platform' of https://githu…
djabarovgeorge May 12, 2026
6d12930
feat(integrations): introduce integration kind distinction and update…
djabarovgeorge May 12, 2026
40980db
feat(agents): enhance managed agent provisioning and runtime configur…
djabarovgeorge May 12, 2026
8796d4e
feat(agents): update agent creation DTO and use case validation
djabarovgeorge May 12, 2026
276e794
refactor(workflow): replace NotificationChannelTypeEnum with ChannelT…
djabarovgeorge May 12, 2026
18d9a90
refactor(tests): update managed agent E2E tests to stub AgentRuntimeF…
djabarovgeorge May 12, 2026
7ddbc16
fix(tests): update managed agent E2E test to correct MCP server URL
djabarovgeorge May 12, 2026
f1186b8
chore: update hash
djabarovgeorge May 12, 2026
b8c3968
Merge branch 'next' into cursor/managed-agents-claude-platform
djabarovgeorge May 12, 2026
29b5c99
fix(api-service): enforce MCP catalog on agent runtime config PATCH (…
djabarovgeorge May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .source
2 changes: 1 addition & 1 deletion apps/api/src/app/agents/agent-analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function trackAgentIntegrationConnected(
integrationId: string;
integrationIdentifier: string;
providerId: string;
channel: string;
channel?: string;
connectionSource: 'existing_integration' | 'novu_email_provisioned';
}
): void {
Expand Down
95 changes: 93 additions & 2 deletions apps/api/src/app/agents/agents.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Post,
Put,
Query,
UseFilters,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
Expand All @@ -28,6 +29,7 @@ import { ExternalApiAccessible } from '../auth/framework/external-api.decorator'
import { ThrottlerCategory } from '../rate-limiting/guards';
import {
ApiCommonResponses,
ApiConflictResponse,
ApiNoContentResponse,
ApiNotFoundResponse,
ApiResponse,
Expand All @@ -37,11 +39,13 @@ import {
AddAgentIntegrationRequestDto,
AgentIntegrationResponseDto,
AgentResponseDto,
AgentRuntimeConfigResponseDto,
CreateAgentRequestDto,
ListAgentIntegrationsQueryDto,
ListAgentIntegrationsResponseDto,
ListAgentsQueryDto,
ListAgentsResponseDto,
PatchAgentRuntimeConfigRequestDto,
UpdateAgentBridgeRequestDto,
UpdateAgentIntegrationRequestDto,
UpdateAgentRequestDto,
Expand All @@ -53,6 +57,7 @@ import {
SendWhatsAppTestTemplateRequestDto,
SendWhatsAppTestTemplateResponseDto,
} from './dtos/send-whatsapp-test-template.dto';
import { AgentRuntimeExceptionFilter } from './filters/agent-runtime-exception.filter';
import { AgentConversationEnabledGuard } from './guards/agent-conversation-enabled.guard';
import { AddAgentIntegrationCommand } from './usecases/add-agent-integration/add-agent-integration.command';
import { AddAgentIntegration } from './usecases/add-agent-integration/add-agent-integration.usecase';
Expand All @@ -64,6 +69,8 @@ import { DeleteAgentCommand } from './usecases/delete-agent/delete-agent.command
import { DeleteAgent } from './usecases/delete-agent/delete-agent.usecase';
import { GetAgentCommand } from './usecases/get-agent/get-agent.command';
import { GetAgent } from './usecases/get-agent/get-agent.usecase';
import { GetAgentRuntimeConfigCommand } from './usecases/get-agent-runtime-config/get-agent-runtime-config.command';
import { GetAgentRuntimeConfig } from './usecases/get-agent-runtime-config/get-agent-runtime-config.usecase';
import { type AgentEmojiEntry, ListAgentEmoji } from './usecases/list-agent-emoji/list-agent-emoji.usecase';
import { ListAgentIntegrationsCommand } from './usecases/list-agent-integrations/list-agent-integrations.command';
import { ListAgentIntegrations } from './usecases/list-agent-integrations/list-agent-integrations.usecase';
Expand All @@ -81,6 +88,8 @@ import { UpdateAgentCommand } from './usecases/update-agent/update-agent.command
import { UpdateAgent } from './usecases/update-agent/update-agent.usecase';
import { UpdateAgentIntegrationCommand } from './usecases/update-agent-integration/update-agent-integration.command';
import { UpdateAgentIntegration } from './usecases/update-agent-integration/update-agent-integration.usecase';
import { UpdateAgentRuntimeConfigCommand } from './usecases/update-agent-runtime-config/update-agent-runtime-config.command';
import { UpdateAgentRuntimeConfig } from './usecases/update-agent-runtime-config/update-agent-runtime-config.usecase';

@ThrottlerCategory(ApiRateLimitCategoryEnum.CONFIGURATION)
@ApiCommonResponses()
Expand All @@ -103,6 +112,8 @@ export class AgentsController {
private readonly listAgentEmojiUsecase: ListAgentEmoji,
private readonly sendAgentTestEmailUsecase: SendAgentTestEmail,
private readonly sendAgentWelcomeMessageUsecase: SendAgentWelcomeMessage,
private readonly getAgentRuntimeConfigUsecase: GetAgentRuntimeConfig,
private readonly updateAgentRuntimeConfigUsecase: UpdateAgentRuntimeConfig,
private readonly configureWhatsAppWebhookUsecase: ConfigureWhatsAppWebhook,
private readonly sendWhatsAppTestTemplateUsecase: SendWhatsAppTestTemplate
) {}
Expand All @@ -126,6 +137,7 @@ export class AgentsController {
description: 'Creates an agent scoped to the current environment. The identifier must be unique per environment.',
})
@RequirePermissions(PermissionsEnum.AGENT_WRITE)
@UseFilters(AgentRuntimeExceptionFilter)
createAgent(@UserSession() user: UserSessionData, @Body() body: CreateAgentRequestDto): Promise<AgentResponseDto> {
return this.createAgentUsecase.execute(
CreateAgentCommand.create({
Expand All @@ -136,6 +148,9 @@ export class AgentsController {
identifier: body.identifier,
description: body.description,
active: body.active,
runtime: body.runtime,
managedRuntime: body.managedRuntime,
creationSource: body.creationSource,
})
);
}
Expand Down Expand Up @@ -478,7 +493,10 @@ export class AgentsController {
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({
summary: 'Delete agent',
description: 'Deletes an agent by identifier and removes all agent-integration links.',
description:
'Deletes an agent by identifier and removes all agent-integration links. ' +
'For managed-runtime agents, pass `deleteFromProvider=true` to also archive the agent on the provider side (e.g. Anthropic). ' +
'By default only the Novu record is deleted and the provider agent is left intact.',
})
@ApiNoContentResponse({
description: 'The agent was deleted.',
Expand All @@ -487,13 +505,86 @@ export class AgentsController {
description: 'The agent was not found.',
})
@RequirePermissions(PermissionsEnum.AGENT_WRITE)
deleteAgent(@UserSession() user: UserSessionData, @Param('identifier') identifier: string): Promise<void> {
deleteAgent(
@UserSession() user: UserSessionData,
@Param('identifier') identifier: string,
@Query('deleteFromProvider') deleteFromProvider?: string
): Promise<void> {
return this.deleteAgentUsecase.execute(
DeleteAgentCommand.create({
userId: user._id,
environmentId: user.environmentId,
organizationId: user.organizationId,
identifier,
deleteFromProvider: deleteFromProvider === 'true',
})
);
}

@Get('/:identifier/runtime/config')
@ApiResponse(AgentRuntimeConfigResponseDto, 200)
@ApiOperation({
summary: 'Get agent runtime config',
description:
'Fetches the live runtime configuration for a managed agent from the provider ' +
'(model, system prompt, MCP servers, tools). Returns 422 for self-hosted agents.',
})
@ApiNotFoundResponse({ description: 'Agent or its runtime integration was not found.' })
@ApiConflictResponse({
description:
'AGENT_RUNTIME_DRIFT — the agent record exists in Novu but the provider reports it as deleted or unreachable. ' +
'Re-provision or delete the agent.',
})
@RequirePermissions(PermissionsEnum.AGENT_READ)
@UseFilters(AgentRuntimeExceptionFilter)
Comment thread
djabarovgeorge marked this conversation as resolved.
getAgentRuntimeConfig(
@UserSession() user: UserSessionData,
@Param('identifier') identifier: string
): Promise<AgentRuntimeConfigResponseDto> {
return this.getAgentRuntimeConfigUsecase.execute(
GetAgentRuntimeConfigCommand.create({
userId: user._id,
environmentId: user.environmentId,
organizationId: user.organizationId,
identifier,
})
);
}

@Patch('/:identifier/runtime/config')
@ApiResponse(AgentRuntimeConfigResponseDto, 200)
@ApiOperation({
summary: 'Update agent runtime config',
description:
'Applies a partial update to the managed agent runtime config on the provider. ' +
'Accepts any combination of model, systemPrompt, mcpServers, tools, and skills. ' +
'Server-side diffing issues the minimal set of provider API calls. ' +
'An empty body is accepted and returns the current config unchanged.',
})
@ApiNotFoundResponse({ description: 'Agent or its runtime integration was not found.' })
@ApiConflictResponse({
description:
'AGENT_RUNTIME_DRIFT — the agent record exists in Novu but the provider reports it as deleted or unreachable. ' +
'Re-provision or delete the agent.',
})
@RequirePermissions(PermissionsEnum.AGENT_WRITE)
@UseFilters(AgentRuntimeExceptionFilter)
updateAgentRuntimeConfig(
@UserSession() user: UserSessionData,
@Param('identifier') identifier: string,
@Body() body: PatchAgentRuntimeConfigRequestDto
): Promise<AgentRuntimeConfigResponseDto> {
return this.updateAgentRuntimeConfigUsecase.execute(
UpdateAgentRuntimeConfigCommand.create({
userId: user._id,
environmentId: user.environmentId,
organizationId: user.organizationId,
identifier,
model: body.model,
systemPrompt: body.systemPrompt,
mcpServers: body.mcpServers,
tools: body.tools,
skills: body.skills,
})
);
}
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/app/agents/agents.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ChannelEndpointRepository,
ConversationActivityRepository,
ConversationRepository,
IntegrationRepository,
} from '@novu/dal';

import { AuthModule } from '../auth/auth.module';
Expand All @@ -12,6 +13,7 @@ import { SharedModule } from '../shared/shared.module';
import { AgentEmailActionsController } from './agent-email-actions.controller';
import { AgentsController } from './agents.controller';
import { AgentsWebhookController } from './agents-webhook.controller';
import { AgentRuntimeExceptionFilter } from './filters/agent-runtime-exception.filter';
import { AgentAttachmentStorage } from './services/agent-attachment-storage.service';
import { AgentConfigResolver } from './services/agent-config-resolver.service';
import { AgentConversationService } from './services/agent-conversation.service';
Expand All @@ -27,10 +29,12 @@ import { USE_CASES } from './usecases';
controllers: [AgentsController, AgentsWebhookController, AgentEmailActionsController],
providers: [
...USE_CASES,
AgentRuntimeExceptionFilter,
ChannelConnectionRepository,
ChannelEndpointRepository,
ConversationRepository,
ConversationActivityRepository,
IntegrationRepository,
AgentAttachmentStorage,
AgentConfigResolver,
AgentSubscriberResolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ export class AgentIntegrationResponseIntegrationDto {
@ApiProperty()
providerId: string;

@ApiProperty({ enum: ChannelTypeEnum, enumName: 'ChannelTypeEnum' })
channel: ChannelTypeEnum;
@ApiPropertyOptional({
description: 'Delivery channel; not set for agent-runtime integrations.',
enum: ChannelTypeEnum,
enumName: 'ChannelTypeEnum',
})
channel?: ChannelTypeEnum;

@ApiProperty()
active: boolean;
Expand Down
10 changes: 7 additions & 3 deletions apps/api/src/app/agents/dtos/agent-integration-summary.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { ChannelTypeEnum } from '@novu/shared';

export class AgentIntegrationSummaryDto {
Expand All @@ -14,8 +14,12 @@ export class AgentIntegrationSummaryDto {
@ApiProperty()
identifier: string;

@ApiProperty({ enum: ChannelTypeEnum, enumName: 'ChannelTypeEnum' })
channel: ChannelTypeEnum;
@ApiPropertyOptional({
description: 'Delivery channel; not set for agent-runtime integrations.',
enum: ChannelTypeEnum,
enumName: 'ChannelTypeEnum',
})
channel?: ChannelTypeEnum;

@ApiProperty()
active: boolean;
Expand Down
31 changes: 31 additions & 0 deletions apps/api/src/app/agents/dtos/agent-response.dto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import type { AgentRuntime } from '@novu/shared';
import { AgentCreationSourceEnum } from '@novu/shared';

import { AgentBehaviorDto } from './agent-behavior.dto';
import { AgentIntegrationSummaryDto } from './agent-integration-summary.dto';

export class ManagedRuntimeResponseDto {
@ApiProperty()
providerId: string;

@ApiProperty()
integrationId: string;

@ApiProperty()
externalAgentId: string;
}

export class AgentResponseDto {
@ApiProperty()
_id: string;
Expand Down Expand Up @@ -31,6 +44,18 @@ export class AgentResponseDto {
@ApiPropertyOptional({ description: 'Whether the dev bridge override is active' })
devBridgeActive?: boolean;

@ApiPropertyOptional({
enum: ['self-hosted', 'managed'],
description: 'Whether the agent brain is self-hosted (bridge) or managed by a third-party provider',
})
runtime?: AgentRuntime;

@ApiPropertyOptional({
type: ManagedRuntimeResponseDto,
description: 'Present when runtime is "managed". Contains provider and external identifiers.',
})
managedRuntime?: ManagedRuntimeResponseDto;

@ApiProperty()
_environmentId: string;

Expand All @@ -43,6 +68,12 @@ export class AgentResponseDto {
@ApiProperty()
updatedAt: string;

@ApiPropertyOptional({
enum: AgentCreationSourceEnum,
description: 'Which section of the Novu Dashboard was used to create this agent.',
})
creationSource?: AgentCreationSourceEnum;

@ApiPropertyOptional({ type: [AgentIntegrationSummaryDto] })
integrations?: AgentIntegrationSummaryDto[];
}
Loading
Loading