@@ -7,17 +7,36 @@ const { getBaseBranch } = require("./get_base_branch.cjs");
77const { isStagedMode } = require ( "./safe_output_helpers.cjs" ) ;
88const { generateStagedPreview } = require ( "./staged_preview.cjs" ) ;
99
10- const fs = require ( "fs" ) ;
11- const path = require ( "path" ) ;
12-
1310/**
1411 * Module-level state — populated by handleMessage(), read by the exported getters below.
1512 * Using module-level variables (rather than closure-only state) allows the handler
1613 * manager to read final output values after all messages have been processed.
17- * @type {Array<{number : string, url: string, success: boolean, error?: string}> }
14+ * @type {Array<{id : string, url: string, success: boolean, error?: string}> }
1815 */
1916let _allResults = [ ] ;
2017
18+ /**
19+ * Create a dedicated GitHub client for create-agent-session operations.
20+ *
21+ * Token precedence:
22+ * 1. config["github-token"] — per-handler PAT configured in the workflow frontmatter
23+ * 2. GH_AW_AGENT_SESSION_TOKEN — agent token injected by the compiler as a step env var
24+ * (evaluates to: GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN)
25+ * 3. global github — step-level token (fallback when no agent token is available)
26+ *
27+ * @param {Object } config - Handler configuration
28+ * @returns {Promise<Object> } Authenticated GitHub client
29+ */
30+ async function createAgentSessionGitHubClient ( config ) {
31+ const token = config [ "github-token" ] || process . env . GH_AW_AGENT_SESSION_TOKEN ;
32+ if ( ! token ) {
33+ core . debug ( "No dedicated agent token configured — using step-level github client for create-agent-session operations" ) ;
34+ return github ;
35+ }
36+ core . info ( "Using dedicated github client for create-agent-session operations" ) ;
37+ return global . getOctokit ( token ) ;
38+ }
39+
2140/**
2241 * Handler factory for create-agent-session safe output.
2342 *
@@ -41,17 +60,20 @@ async function main(config = {}) {
4160 core . info ( `Default target repo: ${ defaultTargetRepo } ` ) ;
4261 if ( allowedRepos . size > 0 ) core . info ( `Allowed repos: ${ [ ...allowedRepos ] . join ( ", " ) } ` ) ;
4362
63+ // Create a dedicated Octokit instance using the agent token
64+ const githubClient = await createAgentSessionGitHubClient ( config ) ;
65+
4466 /**
4567 * Process a single create_agent_session message.
4668 * @param {Object } message - The agent output message
47- * @returns {Promise<{success: boolean, number ?: string, url?: string, error?: string, skipped?: boolean}> }
69+ * @returns {Promise<{success: boolean, id ?: string, url?: string, error?: string, skipped?: boolean}> }
4870 */
4971 return async function handleMessage ( message ) {
5072 const taskDescription = message . body ;
5173
5274 if ( ! taskDescription || taskDescription . trim ( ) === "" ) {
5375 core . warning ( "Agent task description is empty, skipping" ) ;
54- _allResults . push ( { number : "" , url : "" , success : false , error : "Empty task description" } ) ;
76+ _allResults . push ( { id : "" , url : "" , success : false , error : "Empty task description" } ) ;
5577 return { success : false , error : "Empty task description" } ;
5678 }
5779
@@ -60,7 +82,7 @@ async function main(config = {}) {
6082 if ( ! repoResult . success ) {
6183 const errorMsg = `E004: ${ repoResult . error } ` ;
6284 core . error ( errorMsg ) ;
63- _allResults . push ( { number : "" , url : "" , success : false , error : repoResult . error } ) ;
85+ _allResults . push ( { id : "" , url : "" , success : false , error : repoResult . error } ) ;
6486 return { success : false , error : repoResult . error } ;
6587 }
6688 const { repo : effectiveRepo , repoParts } = repoResult ;
@@ -87,91 +109,50 @@ async function main(config = {}) {
87109 }
88110
89111 try {
90- // Write task description to a temporary file
91- const tmpDir = "/tmp/gh-aw" ;
92- if ( ! fs . existsSync ( tmpDir ) ) {
93- fs . mkdirSync ( tmpDir , { recursive : true } ) ;
94- }
95-
96- const taskIndex = _allResults . length + 1 ;
97- const taskFile = path . join ( tmpDir , `agent-task-description-${ taskIndex } .md` ) ;
98- fs . writeFileSync ( taskFile , taskDescription , "utf8" ) ;
99- core . info ( `Task ${ taskIndex } : Task description written to ${ taskFile } ` ) ;
112+ core . info ( `Task ${ _allResults . length + 1 } : Creating agent session in ${ effectiveRepo } on branch ${ baseBranch } ` ) ;
113+
114+ // Call the GitHub REST API to start a task
115+ // Reference: https://docs.github.com/en/rest/agent-tasks/agent-tasks?apiVersion=2026-03-10#start-a-task
116+ const response = await githubClient . request ( "POST /agents/repos/{owner}/{repo}/tasks" , {
117+ owner : repoParts . owner ,
118+ repo : repoParts . repo ,
119+ prompt : taskDescription ,
120+ base_ref : baseBranch ,
121+ headers : { "X-GitHub-Api-Version" : "2026-03-10" } ,
122+ } ) ;
100123
101- // Build gh agent-task create command
102- const ghArgs = [ "agent-task" , "create" , "--from-file" , taskFile , "--base" , baseBranch ] ;
124+ const task = response . data ;
125+ const taskId = task . id || "" ;
126+ const taskUrl = task . html_url || task . url || "" ;
103127
104- const contextRepo = `${ context . repo . owner } /${ context . repo . repo } ` ;
105- if ( effectiveRepo !== contextRepo ) {
106- ghArgs . push ( "--repo" , effectiveRepo ) ;
107- }
108-
109- core . info ( `Task ${ taskIndex } : Creating agent session with command: gh ${ ghArgs . join ( " " ) } ` ) ;
110-
111- // Determine token: prefer per-handler token, fall back to step-level token
112- const ghToken = config [ "github-token" ] || process . env . GH_AW_AGENT_SESSION_TOKEN || process . env . GITHUB_TOKEN || "" ;
113-
114- // Execute gh agent-task create command
115- let taskOutput ;
116- try {
117- taskOutput = await exec . getExecOutput ( "gh" , ghArgs , {
118- silent : false ,
119- ignoreReturnCode : false ,
120- env : {
121- ...process . env ,
122- GH_TOKEN : ghToken ,
123- } ,
124- } ) ;
125- } catch ( execError ) {
126- const errorMessage = execError instanceof Error ? execError . message : String ( execError ) ;
127-
128- // Check for authentication/permission errors
129- if ( errorMessage . includes ( "authentication" ) || errorMessage . includes ( "permission" ) || errorMessage . includes ( "forbidden" ) || errorMessage . includes ( "401" ) || errorMessage . includes ( "403" ) ) {
130- core . error ( `Task ${ taskIndex } : Failed to create agent session due to authentication/permission error.` ) ;
131- core . error ( `The default GITHUB_TOKEN may not have permission to create agent sessions.` ) ;
132- core . error ( `Configure a Personal Access Token (PAT) using the handler's github-token setting or GH_AW_AGENT_SESSION_TOKEN.` ) ;
133- core . error ( `See documentation: https://github.github.com/gh-aw/reference/safe-outputs/#agent-task-creation-create-agent-session` ) ;
134- } else {
135- core . error ( `Task ${ taskIndex } : Failed to create agent session: ${ errorMessage } ` ) ;
136- }
137- _allResults . push ( { number : "" , url : "" , success : false , error : errorMessage } ) ;
138- return { success : false , error : errorMessage } ;
139- }
128+ core . info ( `✅ Successfully created agent session ${ taskId } ` ) ;
129+ _allResults . push ( { id : taskId , url : taskUrl , success : true } ) ;
130+ return { success : true , id : taskId , url : taskUrl } ;
131+ } catch ( error ) {
132+ const errorMessage = getErrorMessage ( error ) ;
140133
141- // Parse the output to extract task number and URL.
142- // Expected output format from gh agent-task create is typically:
143- // https://github.com/owner/repo/issues/123
144- const output = taskOutput . stdout . trim ( ) ;
145- core . info ( `Task ${ taskIndex } : Agent task created: ${ output } ` ) ;
146-
147- // Extract task number from URL
148- const urlMatch = output . match ( / g i t h u b \. c o m \/ [ ^ / ] + \/ [ ^ / ] + \/ i s s u e s \/ ( \d + ) / ) ;
149- if ( urlMatch ) {
150- const taskNumber = urlMatch [ 1 ] ;
151- core . info ( `✅ Successfully created agent session #${ taskNumber } ` ) ;
152- _allResults . push ( { number : taskNumber , url : output , success : true } ) ;
153- return { success : true , number : taskNumber , url : output } ;
134+ // Check for authentication/permission errors
135+ if ( errorMessage . includes ( "authentication" ) || errorMessage . includes ( "permission" ) || errorMessage . includes ( "forbidden" ) || errorMessage . includes ( "401" ) || errorMessage . includes ( "403" ) ) {
136+ core . error ( `Failed to create agent session due to authentication/permission error.` ) ;
137+ core . error ( `The default GITHUB_TOKEN may not have permission to create agent sessions.` ) ;
138+ core . error ( `Configure a Personal Access Token (PAT) using the handler's github-token setting or GH_AW_AGENT_SESSION_TOKEN.` ) ;
139+ core . error ( `See documentation: https://github.github.com/gh-aw/reference/safe-outputs/#agent-task-creation-create-agent-session` ) ;
154140 } else {
155- core . warning ( `Task ${ taskIndex } : Could not parse task number from output: ${ output } ` ) ;
156- _allResults . push ( { number : "" , url : output , success : true } ) ;
157- return { success : true , number : "" , url : output } ;
141+ core . error ( `Error creating agent session: ${ errorMessage } ` ) ;
158142 }
159- } catch ( error ) {
160- const errorMessage = getErrorMessage ( error ) ;
161- core . error ( `Error creating agent session: ${ errorMessage } ` ) ;
162- _allResults . push ( { number : "" , url : "" , success : false , error : errorMessage } ) ;
143+ _allResults . push ( { id : "" , url : "" , success : false , error : errorMessage } ) ;
163144 return { success : false , error : errorMessage } ;
164145 }
165146 } ;
166147}
167148
168149/**
169- * Returns the session_number output: the number of the first successfully created session.
150+ * Returns the session_number output: the ID of the first successfully created session.
170151 * @returns {string }
171152 */
172153function getCreateAgentSessionNumber ( ) {
173- const first = _allResults . find ( r => r . success && r . number ) ;
174- return first ? first . number : "" ;
154+ const first = _allResults . find ( r => r . success && r . id ) ;
155+ return first ? first . id : "" ;
175156}
176157
177158/**
@@ -200,8 +181,8 @@ async function writeCreateAgentSessionSummary() {
200181 summaryContent += `✅ Successfully created ${ successResults . length } agent session(s):\n\n` ;
201182 summaryContent += successResults
202183 . map ( ( r , i ) => {
203- if ( r . url && r . number ) {
204- return `- [# ${ r . number } ](${ r . url } )` ;
184+ if ( r . url && r . id ) {
185+ return `- [${ r . id } ](${ r . url } )` ;
205186 } else if ( r . url ) {
206187 return `- [Session ${ i + 1 } ](${ r . url } )` ;
207188 }
0 commit comments