feat(discord): interactive AskUserQuestion with buttons and select menus - #25
Merged
Conversation
Implements channel.InteractiveChannel for the Discord channel so the LLM's AskUserQuestion tool renders as native UI instead of being auto-rejected: - PresentQuestions builds one action row per multiple-choice question: buttons for single-select with <=5 options, select menus otherwise; questions without options fall back to a text prompt - Component interactions (INTERACTION_CREATE message component) are routed back to the waiting agent turn through the manager's AskUserAnswers path; the question message is then edited to disable its components and show the chosen answer, preventing double answers - CustomID namespace tachi:ask:<token>:q<idx>[:o<optIdx>] with a 15-minute pending-state TTL; text replies remain a working fallback - System prompt now tells the LLM it can ask interactive questions
Addresses review findings on the interactive prompt implementation: - Retire pending UI when a prompt is settled via text fallback or cancel: new AskUserAcknowledger hook in pkg/channel, invoked by the manager after routing a fallback answer, so stale buttons can never start an unintended second turn (and non-steered replies from such clicks are now forwarded instead of silently dropped) - Registry now a container.LockedMap: claims are atomic LoadAndDelete (double-clicks can't both deliver), messageID is published under the lock after a successful send (no torn reads), stale entries for a thread are cleaned when presenting a new prompt (bounded growth) - Pre-send validation of Discord hard limits: select menus cap at 25 options (with a typed-fallback hint), message body truncates under 2000 chars, and an empty question list is rejected up front - Complete component interactions via the interaction token (InteractionResponseEdit) with message-edit fallback - CustomID parsing validates q/o segment roles - Prompt copy guides per-question answering; token generation reuses strutil.ShortUUID
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
让 Discord channel 支持
AskUserQuestion交互式提问。此前该工具在 channel 模式下被 manager 自动取消(auto-cancel),agent 永远无法向用户提问;现在会渲染为 Discord 原生 UI(按钮 / select 菜单),点击后答案直接回传给阻塞中的 agent 轮次。变更
feat 6599153 — 交互式 AskUserQuestion
channel.InteractiveChannel(Interactive()+PresentQuestions):INTERACTION_CREATE(message component)事件处理:解析tachi:ask:<token>:q<idx>[:o<optIdx>]CustomID → 构造AskUserAnswers→ 经 manager 现有 handler 路径送回等待中的 agent turn(返回Steered,不重复发消息)fix a6fda93 — 交互生命周期加固(review 反馈)
channel.AskUserAcknowledger钩子,manager 在文字兜底/取消回答投递成功后通知 channel 退役 UI 状态,杜绝"隐形"二次 turn;兜底极罕见路径下非 Steered 回复会被转发而非静默丢弃container.LockedMap承载 pending 状态,claim 为原子LoadAndDelete(双击不会双投);messageID 在锁内发布(无撕裂读);同 thread 旧状态在新提问时清理(有界增长)InteractionResponseEdit),失败 fallback 消息编辑测试
go test ./...与go test -race ./channel/discord/ ./channel/manager/通过相关文档
docs/2026-07-11-discord-channel-design.md(Phase 3 遗留项:AskUserQuestion 组件)