Skip to content

feat(tools): add create_discord_thread tool#976

Open
tarrencev wants to merge 2 commits intonextlevelbuilder:devfrom
cartridge-gg:feat/create-discord-thread
Open

feat(tools): add create_discord_thread tool#976
tarrencev wants to merge 2 commits intonextlevelbuilder:devfrom
cartridge-gg:feat/create-discord-thread

Conversation

@tarrencev
Copy link
Copy Markdown

@tarrencev tarrencev commented Apr 21, 2026

Summary

Adds a new agent tool create_discord_thread that 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:

  1. Snowflake numeric IDs. channel and channel_id now use argString (same numeric-coercion helper used by message_id) so a Discord ID emitted by the LLM as a JSON number doesn't silently fall back to the invoking context.
  2. Whitespace-only name. name is 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.
  3. auto_archive_minutes enum validation. Validated against Discord's allowed set {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 in internal/tools/shell_abort_test.go not touched by this PR)
  • Manual: invoke the tool in a text channel, forum channel, and DM (should be rejected) — confirm the dispatch produces the right thread kind in each case

🤖 Generated with Claude Code

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.
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>
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