feat(tools): add create_discord_thread tool#976
Open
tarrencev wants to merge 2 commits intonextlevelbuilder:devfrom
Open
feat(tools): add create_discord_thread tool#976tarrencev wants to merge 2 commits intonextlevelbuilder:devfrom
tarrencev wants to merge 2 commits intonextlevelbuilder:devfrom
Conversation
Lets agents create Discord threads (text channels) and posts (forum channels) on Discord-originated sessions. Mirrors the `list_group_members` pattern: channel-specific tool routed through `channels.Manager` by name, gated by `RequiredChannelTypes` + `ChannelTenantCheckerAware` for cross-tenant isolation. The new thread's ID is returned so the LLM can follow up via the existing `message` tool (thread IDs are just channel IDs to the Discord API) — no new outbound send path needed. - Dispatches to `MessageThreadStartComplex` (message-rooted), `ThreadStartComplex` (standalone text), or `ForumThreadStartComplex` (forum post, requires initial message). - Rejects DM / GroupDM parents at both the tool layer (peer kind) and the Discord layer (parent channel type). - Uses `session.State.Channel` first, falls back to REST on cache miss. - 26 unit tests: arg validation, DM rejection, tenant mismatch regression, full dispatch-branch coverage via an injectable threadAPI seam. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tarrencev
added a commit
to cartridge-gg/goclaw
that referenced
this pull request
Apr 21, 2026
Integrates the create_discord_thread tool ahead of upstream PR nextlevelbuilder#976 so our dev branch carries the feature while the PR is in review. When the upstream PR lands we will reset dev to upstream/dev and re-merge any features still in flight.
3 tasks
Review follow-ups — three small robustness fixes on the input path:
1. channel / channel_id now go through argString so a Discord snowflake
ID emitted by the LLM as a JSON number doesn't silently fall back to
the invoking context. Matches the numeric-coercion path already used
by message_id (and by the message tool).
2. name is trimmed before the 1-100-character length check. Previously
a whitespace-only name like " " passed the local check (rune length
3) and was rejected only by Discord with a generic 400. Now the LLM
gets an immediate, actionable error.
3. auto_archive_minutes validated against Discord's allowed set
{60, 1440, 4320, 10080}. The previous code forwarded any int to
Discord which returned a 400; validating locally surfaces a clearer
error and saves the HTTP round-trip.
Tests:
- TestCreateDiscordThread_NameValidation gets a "whitespace-only" case.
- TestCreateDiscordThread_AutoArchiveEnum covers both an invalid value
(rejected pre-API) and each of the four allowed values (pass-through).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Adds a new agent tool
create_discord_threadthat creates Discord threads (text channel threads, message-rooted threads, or forum posts) via a clean channels.Manager dispatch layer. Mirrors the tenant-gating pattern used by the message tool. Ships with the full tool + channel impl + wiring + tests.Review (added commit
182efd2d)Pre-landing review surfaced three small robustness issues on the input path, now fixed:
channelandchannel_idnow useargString(same numeric-coercion helper used bymessage_id) so a Discord ID emitted by the LLM as a JSON number doesn't silently fall back to the invoking context.nameis trimmed before the 1-100-character length check. Previously" "(rune length 3) passed the local check and was rejected only by Discord with a generic 400.{60, 1440, 4320, 10080}at the tool layer so the LLM gets an actionable error instead of a Discord 400.Tests added: whitespace-only name case; auto-archive enum (invalid value rejected pre-API, and each of the four allowed values pass-through).
Test plan
go build ./...+go build -tags sqliteonly ./...+go vet ./...go test ./internal/channels/discord/... ./internal/tools/— discord-thread tests green (unrelated pre-existing shell-abort flake ininternal/tools/shell_abort_test.gonot touched by this PR)🤖 Generated with Claude Code