@@ -58,6 +58,35 @@ type AgentProvider = "opencode" | "claudecode" | "codex" | "kimi" | "kiro" | "ki
5858type StatusMessageFormat = "aggressive" | "medium" | "minimum" ;
5959type GitStrategy = "default" | "worktree" ;
6060
61+ type SlackActionBody = {
62+ actions ?: Array < {
63+ value ?: string ;
64+ selected_option ?: {
65+ value ?: string ;
66+ } ;
67+ } > ;
68+ channel ?: {
69+ id ?: string ;
70+ } ;
71+ user ?: {
72+ id ?: string ;
73+ } ;
74+ trigger_id ?: string ;
75+ view ?: {
76+ private_metadata ?: string ;
77+ id ?: string ;
78+ hash ?: string ;
79+ state ?: {
80+ values ?: Record < string , Record < string , { value ?: string ; selected_option ?: { value ?: string } } > > ;
81+ } ;
82+ } ;
83+ message ?: {
84+ thread_ts ?: string ;
85+ ts ?: string ;
86+ text ?: string ;
87+ } ;
88+ } ;
89+
6190const AGENT_PROVIDERS : AgentProvider [ ] = [ "opencode" , "claudecode" , "codex" , "kimi" , "kiro" , "kilo" , "qwen" , "goose" ] ;
6291
6392const AGENT_PROVIDER_LABELS : Record < AgentProvider , string > = {
@@ -109,6 +138,38 @@ function toSelectableProvider(provider: "opencode" | "claudecode" | "codex" | "k
109138 return parseAgentProvider ( provider ) ;
110139}
111140
141+ function getActionChannelId ( body : SlackActionBody ) : string | undefined {
142+ return body . actions ?. [ 0 ] ?. value ?? body . channel ?. id ;
143+ }
144+
145+ function getActionUserId ( body : SlackActionBody ) : string | undefined {
146+ return body . user ?. id ;
147+ }
148+
149+ function getActionTriggerId ( body : SlackActionBody ) : string | undefined {
150+ return body . trigger_id ;
151+ }
152+
153+ function getActionSelectedOptionValue ( body : SlackActionBody ) : string | undefined {
154+ return body . actions ?. [ 0 ] ?. selected_option ?. value ;
155+ }
156+
157+ function getActionViewMetadata ( body : SlackActionBody ) : string | undefined {
158+ return body . view ?. private_metadata ;
159+ }
160+
161+ function getActionThreadTs ( body : SlackActionBody ) : string | undefined {
162+ return body . message ?. thread_ts || body . message ?. ts ;
163+ }
164+
165+ function getActionMessageTs ( body : SlackActionBody ) : string | undefined {
166+ return body . message ?. ts ;
167+ }
168+
169+ function getActionMessageText ( body : SlackActionBody ) : string {
170+ return body . message ?. text || "Question" ;
171+ }
172+
112173function buildSettingsModal ( params : {
113174 channelId : string ;
114175 enabledProviders : AgentProvider [ ] ;
@@ -417,9 +478,11 @@ export function setupInteractiveHandlers(): void {
417478 slackApp . action ( SETTINGS_LAUNCH_ACTION , async ( { ack, body, client } ) => {
418479 await ack ( ) ;
419480
420- const channelId = ( body as any ) . actions ?. [ 0 ] ?. value
421- ?? ( body as any ) . channel ?. id ;
422- if ( ! channelId ) return ;
481+ const actionBody = body as SlackActionBody ;
482+
483+ const channelId = getActionChannelId ( actionBody ) ;
484+ const triggerId = getActionTriggerId ( actionBody ) ;
485+ if ( ! channelId || ! triggerId ) return ;
423486
424487 try {
425488 await startOpenCodeServer ( ) ;
@@ -448,18 +511,20 @@ export function setupInteractiveHandlers(): void {
448511 } ) ;
449512
450513 await client . views . open ( {
451- trigger_id : ( body as any ) . trigger_id ,
514+ trigger_id : triggerId ,
452515 view,
453516 } ) ;
454517 } ) ;
455518
456519 slackApp . action ( GITHUB_LAUNCH_ACTION , async ( { ack, body, client } ) => {
457520 await ack ( ) ;
458521
459- const channelId = ( body as any ) . actions ?. [ 0 ] ?. value
460- ?? ( body as any ) . channel ?. id ;
461- const userId = ( body as any ) . user ?. id ;
462- if ( ! channelId || ! userId ) return ;
522+ const actionBody = body as SlackActionBody ;
523+
524+ const channelId = getActionChannelId ( actionBody ) ;
525+ const userId = getActionUserId ( actionBody ) ;
526+ const triggerId = getActionTriggerId ( actionBody ) ;
527+ if ( ! channelId || ! userId || ! triggerId ) return ;
463528
464529 const info = getGitHubInfoForUser ( userId ) ;
465530 const view = buildGitHubTokenModal ( {
@@ -471,16 +536,19 @@ export function setupInteractiveHandlers(): void {
471536 } ) ;
472537
473538 await client . views . open ( {
474- trigger_id : ( body as any ) . trigger_id ,
539+ trigger_id : triggerId ,
475540 view,
476541 } ) ;
477542 } ) ;
478543
479544 slackApp . action ( GENERAL_SETTINGS_LAUNCH_ACTION , async ( { ack, body, client } ) => {
480545 await ack ( ) ;
481546
482- const channelId = ( body as any ) . actions ?. [ 0 ] ?. value
483- ?? ( body as any ) . channel ?. id
547+ const actionBody = body as SlackActionBody ;
548+ const triggerId = getActionTriggerId ( actionBody ) ;
549+ if ( ! triggerId ) return ;
550+
551+ const channelId = getActionChannelId ( actionBody )
484552 ?? "" ;
485553 const generalSettings = getUserGeneralSettings ( ) ;
486554 const view = buildGeneralSettingsModal ( {
@@ -490,18 +558,20 @@ export function setupInteractiveHandlers(): void {
490558 } ) ;
491559
492560 await client . views . open ( {
493- trigger_id : ( body as any ) . trigger_id ,
561+ trigger_id : triggerId ,
494562 view,
495563 } ) ;
496564 } ) ;
497565
498566 slackApp . action ( GENERAL_SYNC_WORKSPACE_ACTION , async ( { ack, body, client } ) => {
499567 await ack ( ) ;
500568
501- const channelId = ( body as any ) . actions ?. [ 0 ] ?. value
502- ?? ( body as any ) . view ?. private_metadata
503- ?? ( body as any ) . channel ?. id ;
504- const userId = ( body as any ) . user ?. id ;
569+ const actionBody = body as SlackActionBody ;
570+
571+ const channelId = getActionChannelId ( actionBody )
572+ ?? getActionViewMetadata ( actionBody )
573+ ?? actionBody . channel ?. id ;
574+ const userId = getActionUserId ( actionBody ) ;
505575 if ( ! channelId || ! userId ) return ;
506576
507577 const workspaces = getWorkspaces ( ) ;
@@ -532,11 +602,14 @@ export function setupInteractiveHandlers(): void {
532602 slackApp . action ( PROVIDER_ACTION , async ( { ack, body, client } ) => {
533603 await ack ( ) ;
534604
535- const view = ( body as any ) . view ;
536- if ( ! view ) return ;
605+ const actionBody = body as SlackActionBody ;
606+
607+ const view = actionBody . view ;
608+ if ( ! view || ! view . id || ! view . hash ) return ;
537609
538610 const channelId = view . private_metadata ;
539- const selectedOption = ( body as any ) . actions ?. [ 0 ] ?. selected_option ?. value ;
611+ if ( ! channelId ) return ;
612+ const selectedOption = getActionSelectedOptionValue ( actionBody ) ;
540613 const selectedProvider = parseAgentProvider ( selectedOption ) ;
541614 if ( selectedProvider === "opencode" ) {
542615 try {
@@ -656,7 +729,8 @@ export function setupInteractiveHandlers(): void {
656729 setChannelBaseBranch ( channelId , baseBranch ) ;
657730 setChannelSystemMessage ( channelId , channelSystemMessage ) ;
658731 } catch ( err ) {
659- const userId = ( body as any ) . user ?. id ;
732+ const actionBody = body as SlackActionBody ;
733+ const userId = getActionUserId ( actionBody ) ;
660734 if ( userId ) {
661735 await client . chat . postEphemeral ( {
662736 channel : channelId ,
@@ -667,7 +741,8 @@ export function setupInteractiveHandlers(): void {
667741 return ;
668742 }
669743
670- const userId = ( body as any ) . user ?. id ;
744+ const actionBody = body as SlackActionBody ;
745+ const userId = getActionUserId ( actionBody ) ;
671746 if ( userId ) {
672747 await client . chat . postEphemeral ( {
673748 channel : channelId ,
@@ -686,9 +761,11 @@ export function setupInteractiveHandlers(): void {
686761
687762 await ack ( ) ;
688763
689- const userId = ( body as any ) . user ?. id ;
690- const channelId = ( body as any ) . view ?. private_metadata
691- ?? ( body as any ) . channel ?. id ;
764+ const actionBody = body as SlackActionBody ;
765+
766+ const userId = getActionUserId ( actionBody ) ;
767+ const channelId = getActionViewMetadata ( actionBody )
768+ ?? actionBody . channel ?. id ;
692769 if ( ! userId || ! channelId ) return ;
693770
694771 try {
@@ -734,8 +811,9 @@ export function setupInteractiveHandlers(): void {
734811 gitStrategy,
735812 } ) ;
736813 } catch ( err ) {
737- const userId = ( body as any ) . user ?. id ;
738- const channelId = view . private_metadata || ( body as any ) . channel ?. id ;
814+ const actionBody = body as SlackActionBody ;
815+ const userId = getActionUserId ( actionBody ) ;
816+ const channelId = view . private_metadata || actionBody . channel ?. id ;
739817 if ( userId && channelId ) {
740818 await client . chat . postEphemeral ( {
741819 channel : channelId ,
@@ -746,8 +824,9 @@ export function setupInteractiveHandlers(): void {
746824 return ;
747825 }
748826
749- const userId = ( body as any ) . user ?. id ;
750- const channelId = view . private_metadata || ( body as any ) . channel ?. id ;
827+ const actionBody = body as SlackActionBody ;
828+ const userId = getActionUserId ( actionBody ) ;
829+ const channelId = view . private_metadata || actionBody . channel ?. id ;
751830 if ( userId && channelId ) {
752831 await client . chat . postEphemeral ( {
753832 channel : channelId ,
@@ -761,18 +840,20 @@ export function setupInteractiveHandlers(): void {
761840 slackApp . action ( / ^ u s e r _ c h o i c e _ \d + $ / , async ( { ack, body, client } ) => {
762841 await ack ( ) ;
763842
764- const action = ( body as any ) . actions ?. [ 0 ] ;
843+ const actionBody = body as SlackActionBody ;
844+
845+ const action = actionBody . actions ?. [ 0 ] ;
765846 const value = action ?. value ;
766- const channel = ( body as any ) . channel ?. id ;
767- const threadTs = ( body as any ) . message ?. thread_ts || ( body as any ) . message ?. ts ;
768- const userId = ( body as any ) . user ?. id ;
769- const messageTs = ( body as any ) . message ?. ts ;
847+ const channel = actionBody . channel ?. id ;
848+ const threadTs = getActionThreadTs ( actionBody ) ;
849+ const userId = getActionUserId ( actionBody ) ;
850+ const messageTs = getActionMessageTs ( actionBody ) ;
770851
771852 if ( ! value || ! channel || ! threadTs ) return ;
772853
773854 // Update the original message to remove buttons (keep question text only)
774855 if ( messageTs ) {
775- const originalText = ( body as any ) . message ?. text || "Question" ;
856+ const originalText = getActionMessageText ( actionBody ) ;
776857 await client . chat . update ( {
777858 channel,
778859 ts : messageTs ,
0 commit comments