Skip to content

feat(connector): add /focus and /ask commands for @-free routing#391

Open
QiliangLi wants to merge 2 commits intozts212653:mainfrom
QiliangLi:feat-focus-ask-commands
Open

feat(connector): add /focus and /ask commands for @-free routing#391
QiliangLi wants to merge 2 commits intozts212653:mainfrom
QiliangLi:feat-focus-ask-commands

Conversation

@QiliangLi
Copy link
Copy Markdown

Summary

Ports the /focus and /ask slash commands from clowder-ai feat branch to enable cat routing without @mentions, addressing the Feishu @mention UX conflict.

Changes

  • /focus <猫名>: Sets a preferred cat for the current thread. Subsequent messages without @mentions will route to this cat.
  • /ask <猫名> <消息>: Single-shot message to a specific cat without changing the default routing.

Features

  • normalizeCatId handles aliases:
    • 宪宪/布偶猫 → opus
    • 砚砚/缅因猫 → codex
    • 烁烁/暹罗猫 → gemini
  • preferredCats stored at thread level via updatePreferredCats
  • ConnectorRouter checks preferredCats before last-active cat fallback
  • Added to FALLBACK_COMMANDS for /commands listing

Files Modified

  • packages/api/src/infrastructure/connectors/ConnectorCommandLayer.ts - Added handleFocus, handleAsk, normalizeCatId methods
  • packages/api/src/infrastructure/connectors/ConnectorRouter.ts - Added /ask forwarding and preferredCats priority logic
  • packages/api/src/infrastructure/connectors/connector-command-helpers.ts - Added /focus and /ask to FALLBACK_COMMANDS

Test Plan

  • ✅ All existing connector command tests pass (58 tests)
  • ✅ /commands shows the new commands
  • ✅ Manually verified routing logic

🤖 Generated with Claude Code

Ports the /focus and /ask slash commands from clowder-ai to enable
cat routing without @mentions, addressing the Feishu @mention UX
conflict.

- /focus <猫名>: Sets a preferred cat for the current thread.
  Subsequent messages without @mentions will route to this cat.
- /ask <猫名> <消息>: Single-shot message to a specific cat
  without changing the default routing.

Features:
- normalizeCatId handles aliases: 宪宪/布偶猫→opus, 砚砚/缅因猫→codex,
  烁烁/暹罗猫→gemini
- preferredCats stored at thread level
- ConnectorRouter checks preferredCats before last-active cat fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@QiliangLi QiliangLi requested a review from zts212653 as a code owner April 8, 2026 16:34
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2307d90d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +56 to +57
{ cmd: '/focus <猫名>', desc: '设置当前 thread 的首选猫' },
{ cmd: '/ask <猫名> <消息>', desc: '单次定向:让指定猫回复这条消息' },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Register new connector commands in the shared registry

Adding /focus and /ask only to FALLBACK_COMMANDS does not make them visible in normal runtime, because buildCommandsList() prefers registry.listBySurface('connector') whenever a CommandRegistry is wired (the default gateway path). As a result, users can execute the new commands but /commands will still omit them in production, which breaks command discoverability.

Useful? React with 👍 / 👎.

Comment on lines +406 to +408
if (this.deps.threadStore.updatePreferredCats) {
await this.deps.threadStore.updatePreferredCats(binding.threadId, [catId]);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail /focus when preferred-cat persistence is unavailable

This branch silently skips persistence if threadStore.updatePreferredCats is missing, but the handler still returns a success message (已设置首选猫). In any deployment or test wiring that uses a thread store without that optional method, /focus appears to succeed while routing behavior never changes, creating a hard-to-debug false-positive state.

Useful? React with 👍 / 👎.

Comment on lines +398 to +401
if (preferredCats && preferredCats.length > 0) {
// Use first preferred cat
targetCatId = preferredCats[0];
preferredCatsApplied = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate preferredCats before forcing routing target

The connector router now takes preferredCats[0] as the target without checking whether that cat is currently routable. If a thread stores a stale/disabled cat ID (for example after catalog or provider changes), every non-mention message in that thread is forced to an invalid target instead of falling back, causing invocation failures rather than a reply.

Useful? React with 👍 / 👎.

- P1: Validate preferredCats exists in catRegistry before routing
  - Prevents routing to stale/disabled cat IDs
  - Falls back to last-active cat if preferred cat invalid

- P2: Register /focus and /ask in CORE_COMMANDS
  - Commands now appear in /commands listing in production
  - Previously only in FALLBACK_COMMANDS (not visible with registry)

- P2: Fail /focus when persistence unavailable
  - Returns error instead of silent success when updatePreferredCats missing
  - Prevents false-positive state where command appears to work but doesn't

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant