33/**
44 * Pi Provider Extension for gh-aw
55 *
6- * Calls the AWF API proxy /reflect endpoint at session start to dynamically
7- * discover the open LLM inference paths configured for this run. This gives
8- * operators runtime visibility into which provider/model combination is active
9- * and verifies that the expected gateway port is reachable before the agent
10- * starts working.
6+ * Registers Pi providers from the AWF-injected environment and calls the AWF
7+ * API proxy /reflect endpoint at session start to dynamically discover the
8+ * open LLM inference paths configured for this run. This gives operators
9+ * runtime visibility into which provider/model combination is active and
10+ * verifies that the expected gateway port is reachable before the agent starts
11+ * working.
1112 *
1213 * When the model uses provider/model format (e.g. "copilot/claude-sonnet-4"),
1314 * the extension logs the matched endpoint so failures can be diagnosed without
1819 * configuration is required.
1920 *
2021 * Configuration (read from environment variables):
21- * PI_MODEL The engine.model value; may be "provider/model" or bare "model".
22+ * GH_AW_PI_MODEL The original engine.model value; may be "provider/model"
23+ * or bare "model". Preferred over PI_MODEL so gh-aw can pass
24+ * model context to extensions without changing Pi CLI behavior.
25+ * PI_MODEL Legacy fallback used when GH_AW_PI_MODEL is not set.
2226 */
2327
2428"use strict" ;
@@ -29,6 +33,18 @@ const { fetchAWFReflect, AWF_API_PROXY_REFLECT_URL, AWF_REFLECT_OUTPUT_PATH, AWF
2933// prettier-ignore
3034const DEFAULT_LOGGER = /** @type {(msg: string) => void } */ ( msg => process . stderr . write ( `[gh-aw/pi-provider] ${ new Date ( ) . toISOString ( ) } ${ msg } \n` ) ) ;
3135
36+ /**
37+ * Return the workflow-configured model string exposed to Pi extensions.
38+ * GH_AW_PI_MODEL takes precedence because gh-aw sets it explicitly for extensions
39+ * while continuing to pass the CLI model via --model. PI_MODEL remains a legacy
40+ * fallback for older callers.
41+ *
42+ * @returns {string }
43+ */
44+ function getConfiguredModel ( ) {
45+ return process . env . GH_AW_PI_MODEL || process . env . PI_MODEL || "" ;
46+ }
47+
3248/**
3349 * Extract the provider prefix from a "provider/model" string.
3450 * Returns an empty string when no slash is present (bare model name).
@@ -66,14 +82,91 @@ function resolveGatewayUrl(provider) {
6682 return `http://api-proxy:${ port } ` ;
6783}
6884
85+ /**
86+ * Register a Pi provider and any aliases.
87+ *
88+ * @param {any } pi
89+ * @param {string[] } names
90+ * @param {Record<string, any> } config
91+ * @param {(msg: string) => void } logger
92+ */
93+ function registerProviderAliases ( pi , names , config , logger ) {
94+ for ( const name of names ) {
95+ pi . registerProvider ( name , config ) ;
96+ logger ( `registered provider=${ name } ` ) ;
97+ }
98+ }
99+
100+ /**
101+ * Register all supported Pi providers discovered from the environment.
102+ *
103+ * @param {any } pi
104+ * @param {(msg: string) => void } logger
105+ * @returns {number }
106+ */
107+ function registerConfiguredProviders ( pi , logger ) {
108+ let registeredCount = 0 ;
109+
110+ const copilotToken = process . env . COPILOT_GITHUB_TOKEN || process . env . GITHUB_TOKEN ;
111+ if ( copilotToken ) {
112+ registerProviderAliases (
113+ pi ,
114+ [ "github-copilot" , "copilot" ] ,
115+ {
116+ apiKey : copilotToken ,
117+ api : "openai-completions" ,
118+ ...( process . env . GITHUB_COPILOT_BASE_URL ? { baseUrl : process . env . GITHUB_COPILOT_BASE_URL } : { } ) ,
119+ } ,
120+ logger
121+ ) ;
122+ registeredCount += 2 ;
123+ }
124+
125+ if ( process . env . ANTHROPIC_API_KEY ) {
126+ registerProviderAliases (
127+ pi ,
128+ [ "anthropic" ] ,
129+ {
130+ apiKey : process . env . ANTHROPIC_API_KEY ,
131+ api : "anthropic" ,
132+ ...( process . env . ANTHROPIC_BASE_URL ? { baseUrl : process . env . ANTHROPIC_BASE_URL } : { } ) ,
133+ } ,
134+ logger
135+ ) ;
136+ registeredCount += 1 ;
137+ }
138+
139+ const openAIKey = process . env . CODEX_API_KEY || process . env . OPENAI_API_KEY ;
140+ if ( openAIKey ) {
141+ registerProviderAliases (
142+ pi ,
143+ [ "openai" , "codex" ] ,
144+ {
145+ apiKey : openAIKey ,
146+ api : "openai-completions" ,
147+ ...( process . env . OPENAI_BASE_URL ? { baseUrl : process . env . OPENAI_BASE_URL } : { } ) ,
148+ } ,
149+ logger
150+ ) ;
151+ registeredCount += 2 ;
152+ }
153+
154+ if ( registeredCount === 0 ) {
155+ logger ( "no provider credentials detected for Pi provider registration" ) ;
156+ }
157+
158+ return registeredCount ;
159+ }
160+
69161/**
70162 * Pi provider extension for gh-aw.
71163 *
72- * Subscribes to the `agent_start` and `agent_end` Pi SDK events and calls the AWF /reflect
73- * endpoint to discover and log the open LLM inference paths before the agent begins its
74- * first turn and again after it finishes. The post-run fetch is the authoritative snapshot
75- * used by the step summary; the pre-run fetch captures the initial proxy state for diagnostics
76- * in case the session exits unexpectedly before reaching `agent_end`.
164+ * Registers providers immediately, then subscribes to the `agent_start` and `agent_end`
165+ * Pi SDK events and calls the AWF /reflect endpoint to discover and log the open LLM
166+ * inference paths before the agent begins its first turn and again after it finishes.
167+ * The post-run fetch is the authoritative snapshot used by the step summary; the pre-run
168+ * fetch captures the initial proxy state for diagnostics in case the session exits
169+ * unexpectedly before reaching `agent_end`.
77170 * Both calls are best-effort: any network or parse error is logged but does not abort the
78171 * agent session.
79172 *
@@ -82,9 +175,10 @@ function resolveGatewayUrl(provider) {
82175 */
83176function piProviderExtension ( pi ) {
84177 const log = DEFAULT_LOGGER ;
178+ registerConfiguredProviders ( pi , log ) ;
85179
86180 pi . on ( "agent_start" , async ( ) => {
87- const model = process . env . PI_MODEL || "" ;
181+ const model = getConfiguredModel ( ) ;
88182 const provider = extractProviderFromModel ( model ) ;
89183
90184 if ( provider ) {
@@ -123,5 +217,7 @@ function piProviderExtension(pi) {
123217}
124218
125219module . exports = piProviderExtension ;
220+ module . exports . getConfiguredModel = getConfiguredModel ;
126221module . exports . extractProviderFromModel = extractProviderFromModel ;
127222module . exports . resolveGatewayUrl = resolveGatewayUrl ;
223+ module . exports . registerConfiguredProviders = registerConfiguredProviders ;
0 commit comments