@@ -71,8 +71,10 @@ import type {
7171 SuggestedReviewersArtefact ,
7272 SuggestedReviewerWriteEntry ,
7373 Task ,
74+ TaskChannel ,
7475 TaskRun ,
7576 TaskRunArtefact ,
77+ TaskThreadMessage ,
7678 UserBasic ,
7779} from "@posthog/shared/domain-types" ;
7880import {
@@ -2120,6 +2122,7 @@ export class PostHogAPIClient {
21202122 createdBy ?: number ;
21212123 originProduct ?: string ;
21222124 internal ?: boolean ;
2125+ channel ?: string ;
21232126 } ) {
21242127 const teamId = await this . getTeamId ( ) ;
21252128 const params : Record < string , string | number | boolean > = {
@@ -2142,6 +2145,10 @@ export class PostHogAPIClient {
21422145 params . internal = true ;
21432146 }
21442147
2148+ if ( options ?. channel ) {
2149+ params . channel = options . channel ;
2150+ }
2151+
21452152 const data = await this . api . get ( `/api/projects/{project_id}/tasks/` , {
21462153 path : { project_id : teamId . toString ( ) } ,
21472154 query : params ,
@@ -2210,6 +2217,7 @@ export class PostHogAPIClient {
22102217 runtime_adapter ?: string | null ;
22112218 model ?: string | null ;
22122219 reasoning_effort ?: string | null ;
2220+ channel ?: string | null ;
22132221 } ,
22142222 ) {
22152223 const teamId = await this . getTeamId ( ) ;
@@ -2259,6 +2267,121 @@ export class PostHogAPIClient {
22592267 } ) ;
22602268 }
22612269
2270+ // Task channels + threads. Not in the generated OpenAPI client yet, so these
2271+ // go through the raw fetcher like the desktop file-system endpoints above.
2272+
2273+ // List backend task channels: all public channels plus the requester's
2274+ // personal "#me" channel (provisioned lazily server-side on first list).
2275+ async getTaskChannels ( ) : Promise < TaskChannel [ ] > {
2276+ const teamId = await this . getTeamId ( ) ;
2277+ const urlPath = `/api/projects/${ teamId } /task_channels/` ;
2278+ const response = await this . api . fetcher . fetch ( {
2279+ method : "get" ,
2280+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2281+ path : urlPath ,
2282+ } ) ;
2283+ if ( ! response . ok ) {
2284+ throw new Error ( `Failed to fetch task channels: ${ response . statusText } ` ) ;
2285+ }
2286+ return ( await response . json ( ) ) as TaskChannel [ ] ;
2287+ }
2288+
2289+ // Resolve-or-create a public channel by name (idempotent server-side).
2290+ async resolveTaskChannel ( name : string ) : Promise < TaskChannel > {
2291+ const teamId = await this . getTeamId ( ) ;
2292+ const urlPath = `/api/projects/${ teamId } /task_channels/` ;
2293+ const response = await this . api . fetcher . fetch ( {
2294+ method : "post" ,
2295+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2296+ path : urlPath ,
2297+ overrides : { body : JSON . stringify ( { name } ) } ,
2298+ } ) ;
2299+ if ( ! response . ok ) {
2300+ throw new Error ( `Failed to resolve task channel: ${ response . statusText } ` ) ;
2301+ }
2302+ return ( await response . json ( ) ) as TaskChannel ;
2303+ }
2304+
2305+ async getTaskThreadMessages ( taskId : string ) : Promise < TaskThreadMessage [ ] > {
2306+ const teamId = await this . getTeamId ( ) ;
2307+ const urlPath = `/api/projects/${ teamId } /tasks/${ taskId } /thread_messages/` ;
2308+ const response = await this . api . fetcher . fetch ( {
2309+ method : "get" ,
2310+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2311+ path : urlPath ,
2312+ } ) ;
2313+ if ( ! response . ok ) {
2314+ throw new Error (
2315+ `Failed to fetch thread messages: ${ response . statusText } ` ,
2316+ ) ;
2317+ }
2318+ return ( await response . json ( ) ) as TaskThreadMessage [ ] ;
2319+ }
2320+
2321+ async createTaskThreadMessage (
2322+ taskId : string ,
2323+ content : string ,
2324+ ) : Promise < TaskThreadMessage > {
2325+ const teamId = await this . getTeamId ( ) ;
2326+ const urlPath = `/api/projects/${ teamId } /tasks/${ taskId } /thread_messages/` ;
2327+ const response = await this . api . fetcher . fetch ( {
2328+ method : "post" ,
2329+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2330+ path : urlPath ,
2331+ overrides : { body : JSON . stringify ( { content } ) } ,
2332+ } ) ;
2333+ if ( ! response . ok ) {
2334+ throw new Error ( `Failed to post thread message: ${ response . statusText } ` ) ;
2335+ }
2336+ return ( await response . json ( ) ) as TaskThreadMessage ;
2337+ }
2338+
2339+ async deleteTaskThreadMessage (
2340+ taskId : string ,
2341+ messageId : string ,
2342+ ) : Promise < void > {
2343+ const teamId = await this . getTeamId ( ) ;
2344+ const urlPath = `/api/projects/${ teamId } /tasks/${ taskId } /thread_messages/${ encodeURIComponent ( messageId ) } /` ;
2345+ const response = await this . api . fetcher . fetch ( {
2346+ method : "delete" ,
2347+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2348+ path : urlPath ,
2349+ } ) ;
2350+ if ( ! response . ok && response . status !== 404 ) {
2351+ throw new Error (
2352+ `Failed to delete thread message: ${ response . statusText } ` ,
2353+ ) ;
2354+ }
2355+ }
2356+
2357+ // Forward a thread message into the task's live run. Task author only; the
2358+ // backend rejects with 400/403 otherwise (surfaced via the error body detail).
2359+ async sendTaskThreadMessageToAgent (
2360+ taskId : string ,
2361+ messageId : string ,
2362+ ) : Promise < TaskThreadMessage > {
2363+ const teamId = await this . getTeamId ( ) ;
2364+ const urlPath = `/api/projects/${ teamId } /tasks/${ taskId } /thread_messages/${ encodeURIComponent ( messageId ) } /send_to_agent/` ;
2365+ const response = await this . api . fetcher . fetch ( {
2366+ method : "post" ,
2367+ url : new URL ( `${ this . api . baseUrl } ${ urlPath } ` ) ,
2368+ path : urlPath ,
2369+ overrides : { body : JSON . stringify ( { } ) } ,
2370+ } ) ;
2371+ if ( ! response . ok ) {
2372+ const errorText = await response . text ( ) . catch ( ( ) => "" ) ;
2373+ let message = `Failed to send message to agent: ${ response . statusText } ` ;
2374+ try {
2375+ const parsed = JSON . parse ( errorText ) as { detail ?: string } ;
2376+ if ( parsed . detail ) message = parsed . detail ;
2377+ } catch {
2378+ if ( errorText ) message = errorText ;
2379+ }
2380+ throw new Error ( message ) ;
2381+ }
2382+ return ( await response . json ( ) ) as TaskThreadMessage ;
2383+ }
2384+
22622385 async sendRunCommand (
22632386 taskId : string ,
22642387 runId : string ,
0 commit comments