Skip to content

feat(MessageComposer): control command sendability#1746

Open
MartinCupela wants to merge 6 commits into
masterfrom
feat/command-sendability
Open

feat(MessageComposer): control command sendability#1746
MartinCupela wants to merge 6 commits into
masterfrom
feat/command-sendability

Conversation

@MartinCupela
Copy link
Copy Markdown
Contributor

@MartinCupela MartinCupela commented May 14, 2026

Goal

Add API that allows to verify, whether the message composer in command mode has state ready to be composed and sent to the server.

This are the rules:

  • ban - requires mention and additional text in order the MessageComposer.isCommandSendable returns true
  • unban - requires mention in order the MessageComposer.isCommandSendable returns true
  • mute - requires mention in order the MessageComposer.isCommandSendable returns true
  • unmute - requires mention in order the MessageComposer.isCommandSendable returns true

It is possible to provide custom command sendability validation function(s) in case the integrators have custom commands and want to apply custom validation rules.

Mentioned users state kept up-to-date on text change
Make sure that the mentioned users array in text composer state is not stale. Until now, this was verified only at the composition time. Now we prune stale user mentions on every text change middleware execution.

MartinCupela and others added 5 commits May 13, 2026 13:12
Add configurable command sendability validators so the composer can block submit for incomplete built-in and custom commands. Reuse the same validation context for send-button gating and final composition validation to keep command readiness consistent.

Co-authored-by: Cursor <cursoragent@cursor.com>
Provide a default ban validator in composer config.
Allow command validator arrays to be replaced via config overrides.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link
Copy Markdown
Contributor

Size Change: +5.46 kB (+1.42%)

Total Size: 390 kB

📦 View Changed
Filename Size Change
dist/cjs/index.browser.js 130 kB +1.81 kB (+1.41%)
dist/cjs/index.node.js 131 kB +1.88 kB (+1.45%)
dist/esm/index.mjs 129 kB +1.77 kB (+1.39%)

compressed-size-action

return { command, ready: true };
}

const reason = stripMentionTokens(commandArgsText, mentionedUsersInText);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something here, but how would this generate a good reason for why the command is currently not sendable ?

return { command, ready: true };
};
export const DEFAULT_COMMANDS_CONFIG: CommandsConfig = {
sendValidators: [defaultCommandSendabilityValidator],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we keep these as an array ? Since the validation step is a middleware anyway, integrators can either add their own additional validation steps (or replace our current one)

const getDisabledRawCommand = (
composer: MessageComposer,
searchSource: CommandSearchSource,
searchSource: Pick<CommandSearchSource, 'query'>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use CommandSearchSource here ? It should still comply

const currentCommand =
composer.textComposer.command ??
getCommandByName(effectiveCommandSearchSource, getRawCommandName(inputText));
if (currentCommand) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (currentCommand) {
if (currentCommand && notifyCommandNotReady({
composer,
sendability: composer.validateCommandSendability(currentCommand, inputText),
})) {

this.pollId ||
!!this.locationComposer.validLocation
const currentCommand = this.textComposer.command;
const commandIsSendable = !currentCommand || this.isCommandSendable(currentCommand);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to extract this in a separate getter please ?

So something like:

get isCommandSendable() {
  const currentCommand = this.textComposer.command;
  return !currentCommand || this.isCommandSendable(currentCommand);
}

export const DEFAULT_COMMANDS_CONFIG: CommandsConfig = {
sendValidators: [defaultCommandSendabilityValidator],
};
export const applyCommandValidatorOverride = (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the command validation anyway goes through a middleware, this might not be needed I think. While convenient for integrators, I think convergence towards a middleware approach would be more appropriate (it would separate the need for overrides as well), unless I'm missing something important here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants