Skip to content

Commit f5c8485

Browse files
committed
feat(api-service): add typing indicator toggle to agent settings fixes NV-7366
Allow users to disable the typing indicator per-agent from the dashboard. Defaults to enabled (preserving current behavior), only an explicit `false` suppresses the `startTyping()` call on inbound messages. Made-with: Cursor
1 parent 242a772 commit f5c8485

File tree

10 files changed

+31
-6
lines changed

10 files changed

+31
-6
lines changed

apps/api/src/app/agents/agents.controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ export class AgentsController {
287287
identifier,
288288
name: body.name,
289289
description: body.description,
290+
typingIndicatorEnabled: body.typingIndicatorEnabled,
290291
})
291292
);
292293
}

apps/api/src/app/agents/dtos/agent-response.dto.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export class AgentResponseDto {
2121
@ApiProperty()
2222
_organizationId: string;
2323

24+
@ApiPropertyOptional({ description: 'Show a typing indicator while the agent is processing a message' })
25+
typingIndicatorEnabled?: boolean;
26+
2427
@ApiProperty()
2528
createdAt: string;
2629

apps/api/src/app/agents/dtos/update-agent-request.dto.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ApiPropertyOptional } from '@nestjs/swagger';
2-
import { IsOptional, IsString } from 'class-validator';
2+
import { IsBoolean, IsOptional, IsString } from 'class-validator';
33

44
export class UpdateAgentRequestDto {
55
@ApiPropertyOptional()
@@ -11,4 +11,9 @@ export class UpdateAgentRequestDto {
1111
@IsString()
1212
@IsOptional()
1313
description?: string;
14+
15+
@ApiPropertyOptional({ description: 'Show a typing indicator while the agent is processing a message' })
16+
@IsBoolean()
17+
@IsOptional()
18+
typingIndicatorEnabled?: boolean;
1419
}

apps/api/src/app/agents/mappers/agent-response.mapper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export function toAgentResponse(agent: AgentEntity): AgentResponseDto {
88
name: agent.name,
99
identifier: agent.identifier,
1010
description: agent.description,
11+
typingIndicatorEnabled: agent.typingIndicatorEnabled,
1112
_environmentId: agent._environmentId,
1213
_organizationId: agent._organizationId,
1314
createdAt: agent.createdAt,

apps/api/src/app/agents/services/agent-credential.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface ResolvedPlatformConfig {
2020
agentIdentifier: string;
2121
integrationIdentifier: string;
2222
integrationId: string;
23+
typingIndicatorEnabled: boolean;
2324
}
2425

2526
@Injectable()
@@ -100,6 +101,7 @@ export class AgentCredentialService {
100101
agentIdentifier: agent.identifier,
101102
integrationIdentifier,
102103
integrationId: integration._id,
104+
typingIndicatorEnabled: agent.typingIndicatorEnabled !== false,
103105
};
104106
}
105107
}

apps/api/src/app/agents/services/agent-inbound-handler.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export class AgentInboundHandler {
7575
organizationId: config.organizationId,
7676
});
7777

78-
await thread.startTyping();
78+
if (config.typingIndicatorEnabled) {
79+
await thread.startTyping();
80+
}
7981

8082
const serializedThread = thread.toJSON() as unknown as Record<string, unknown>;
8183
await this.conversationService.updateChannelThread(

apps/api/src/app/agents/usecases/update-agent/update-agent.command.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
1+
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
22

33
import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command';
44

@@ -14,4 +14,8 @@ export class UpdateAgentCommand extends EnvironmentWithUserCommand {
1414
@IsString()
1515
@IsOptional()
1616
description?: string;
17+
18+
@IsBoolean()
19+
@IsOptional()
20+
typingIndicatorEnabled?: boolean;
1721
}

apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ export class UpdateAgent {
1010
constructor(private readonly agentRepository: AgentRepository) {}
1111

1212
async execute(command: UpdateAgentCommand): Promise<AgentResponseDto> {
13-
if (command.name === undefined && command.description === undefined) {
14-
throw new BadRequestException('At least one of name or description must be provided.');
13+
if (command.name === undefined && command.description === undefined && command.typingIndicatorEnabled === undefined) {
14+
throw new BadRequestException('At least one of name, description, or typingIndicatorEnabled must be provided.');
1515
}
1616

1717
const existing = await this.agentRepository.findOne(
@@ -27,7 +27,7 @@ export class UpdateAgent {
2727
throw new NotFoundException(`Agent with identifier "${command.identifier}" was not found.`);
2828
}
2929

30-
const $set: Record<string, string> = {};
30+
const $set: Record<string, unknown> = {};
3131

3232
if (command.name !== undefined) {
3333
$set.name = command.name;
@@ -37,6 +37,10 @@ export class UpdateAgent {
3737
$set.description = command.description;
3838
}
3939

40+
if (command.typingIndicatorEnabled !== undefined) {
41+
$set.typingIndicatorEnabled = command.typingIndicatorEnabled;
42+
}
43+
4044
await this.agentRepository.updateOne(
4145
{
4246
_id: existing._id,

libs/dal/src/repositories/agent/agent.entity.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export class AgentEntity {
1515

1616
_organizationId: OrganizationId;
1717

18+
typingIndicatorEnabled?: boolean;
19+
1820
createdAt: string;
1921

2022
updatedAt: string;

libs/dal/src/repositories/agent/agent.schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const agentSchema = new Schema<AgentDBModel>(
1414
required: true,
1515
},
1616
description: Schema.Types.String,
17+
typingIndicatorEnabled: Schema.Types.Boolean,
1718
_organizationId: {
1819
type: Schema.Types.ObjectId,
1920
ref: 'Organization',

0 commit comments

Comments
 (0)