From 3366f4b0c2281d8485ec892bd3cd82eeb647b6a3 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 17 Aug 2026 16:06:43 -0700 Subject: [PATCH] feat: support the code_channels manifest feature Add the code_channels feature block to the manifest types so the CLI can read, write, and round-trip it (enabled, optional slash_command_url). Follows the existing feature-block pattern; validation is handled by the manifest validation endpoint. Co-Authored-By: Claude --- internal/shared/types/app_manifest.go | 10 ++++++++++ internal/shared/types/app_manifest_test.go | 23 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/internal/shared/types/app_manifest.go b/internal/shared/types/app_manifest.go index 61755859..743d0feb 100644 --- a/internal/shared/types/app_manifest.go +++ b/internal/shared/types/app_manifest.go @@ -79,6 +79,7 @@ type AppFeatures struct { ManifestSlashCommandsItems []ManifestSlashCommandsItem `json:"slash_commands,omitempty" yaml:"slash_commands,flow,omitempty"` Search *Search `json:"search,omitempty" yaml:"search,flow,omitempty"` RichPreviews *RichPreviews `json:"rich_previews,omitempty" yaml:"rich_previews,flow,omitempty"` + CodeChannels *CodeChannels `json:"code_channels,omitempty" yaml:"code_channels,flow,omitempty"` } type RichPreviews struct { @@ -289,6 +290,15 @@ type Search struct { EnableAIAnswers *bool `json:"enable_ai_answers,omitempty" yaml:"enable_ai_answers,omitempty"` } +// CodeChannels contains settings for apps that create code channels: dedicated +// channels where an agent collaborates with a user on a task. Enabled is a +// pointer so an explicit `false` (feature turned off) is distinguishable from +// an unset field. +type CodeChannels struct { + Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` + SlashCommandURL string `json:"slash_command_url,omitempty" yaml:"slash_command_url,omitempty"` +} + // Workflow defines the structure of a workflow in the app manifest. type Workflow struct { Title string `json:"title" yaml:"title"` diff --git a/internal/shared/types/app_manifest_test.go b/internal/shared/types/app_manifest_test.go index 405be005..cb372c5d 100644 --- a/internal/shared/types/app_manifest_test.go +++ b/internal/shared/types/app_manifest_test.go @@ -250,6 +250,29 @@ func Test_AppManifest_AppFeatures(t *testing.T) { }, want: `{"app_home":{},"bot_user":{"display_name":"business_bot"},"rich_previews":{"entity_types":["slack#/entities/file"]}}`, }, + "includes code channels when provided": { + features: AppFeatures{ + BotUser: BotUser{ + DisplayName: "coding_agent", + }, + CodeChannels: &CodeChannels{ + Enabled: new(true), + SlashCommandURL: "https://example.com/slack/code-channel-commands", + }, + }, + want: `{"app_home":{},"bot_user":{"display_name":"coding_agent"},"code_channels":{"enabled":true,"slash_command_url":"https://example.com/slack/code-channel-commands"}}`, + }, + "serializes code channels disabled explicitly": { + features: AppFeatures{ + BotUser: BotUser{ + DisplayName: "coding_agent", + }, + CodeChannels: &CodeChannels{ + Enabled: new(false), + }, + }, + want: `{"app_home":{},"bot_user":{"display_name":"coding_agent"},"code_channels":{"enabled":false}}`, + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) {