@@ -23,6 +23,7 @@ import (
2323 "github.com/coder/agentapi/lib/httpapi"
2424 "github.com/coder/agentapi/lib/logctx"
2525 "github.com/coder/agentapi/lib/msgfmt"
26+ st "github.com/coder/agentapi/lib/screentracker"
2627 "github.com/coder/agentapi/lib/termexec"
2728)
2829
@@ -145,11 +146,33 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
145146 }
146147
147148 printOpenAPI := viper .GetBool (FlagPrintOpenAPI )
149+ experimentalACP := viper .GetBool (FlagExperimentalACP )
150+
151+ if printOpenAPI && experimentalACP {
152+ return xerrors .Errorf ("flags --%s and --%s are mutually exclusive" , FlagPrintOpenAPI , FlagExperimentalACP )
153+ }
154+
155+ var agentIO st.AgentIO
156+ transport := "pty"
148157 var process * termexec.Process
158+ var acpResult * httpapi.SetupACPResult
159+
149160 if printOpenAPI {
150- process = nil
161+ agentIO = nil
162+ } else if experimentalACP {
163+ var err error
164+ acpResult , err = httpapi .SetupACP (ctx , httpapi.SetupACPConfig {
165+ Program : agent ,
166+ ProgramArgs : argsToPass [1 :],
167+ })
168+ if err != nil {
169+ return xerrors .Errorf ("failed to setup ACP: %w" , err )
170+ }
171+ acpIO := acpResult .AgentIO
172+ agentIO = acpIO
173+ transport = "acp"
151174 } else {
152- process , err = httpapi .SetupProcess (ctx , httpapi.SetupProcessConfig {
175+ proc , err : = httpapi .SetupProcess (ctx , httpapi.SetupProcessConfig {
153176 Program : agent ,
154177 ProgramArgs : argsToPass [1 :],
155178 TerminalWidth : termWidth ,
@@ -159,11 +182,14 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
159182 if err != nil {
160183 return xerrors .Errorf ("failed to setup process: %w" , err )
161184 }
185+ process = proc
186+ agentIO = proc
162187 }
163188 port := viper .GetInt (FlagPort )
164189 srv , err := httpapi .NewServer (ctx , httpapi.ServerConfig {
165190 AgentType : agentType ,
166- Process : process ,
191+ AgentIO : agentIO ,
192+ Transport : httpapi .Transport (transport ),
167193 Port : port ,
168194 ChatBasePath : viper .GetString (FlagChatBasePath ),
169195 AllowedHosts : viper .GetStringSlice (FlagAllowedHosts ),
@@ -195,6 +221,7 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
195221
196222 // Monitor process exit
197223 processExitCh := make (chan error , 1 )
224+ if process != nil {
198225 go func () {
199226 defer close (processExitCh )
200227 defer gracefulCancel ()
@@ -206,6 +233,19 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er
206233 }
207234 }
208235 }()
236+ }
237+ if acpResult != nil {
238+ go func () {
239+ defer close (processExitCh )
240+ defer close (acpResult .Done ) // Signal cleanup goroutine to exit
241+ if err := acpResult .Wait (); err != nil {
242+ processExitCh <- xerrors .Errorf ("ACP process exited: %w" , err )
243+ }
244+ if err := srv .Stop (ctx ); err != nil {
245+ logger .Error ("Failed to stop server" , "error" , err )
246+ }
247+ }()
248+ }
209249
210250 // Start the server
211251 serverErrCh := make (chan error , 1 )
@@ -336,6 +376,7 @@ const (
336376 FlagLoadState = "load-state"
337377 FlagSaveState = "save-state"
338378 FlagPidFile = "pid-file"
379+ FlagExperimentalACP = "experimental-acp"
339380)
340381
341382func CreateServerCmd () * cobra.Command {
@@ -378,6 +419,7 @@ func CreateServerCmd() *cobra.Command {
378419 {FlagLoadState , "" , false , "Load state from state-file on startup (defaults to true when state-file is set)" , "bool" },
379420 {FlagSaveState , "" , false , "Save state to state-file on shutdown (defaults to true when state-file is set)" , "bool" },
380421 {FlagPidFile , "" , "" , "Path to file where the server process ID will be written for shutdown scripts" , "string" },
422+ {FlagExperimentalACP , "" , false , "Use experimental ACP transport instead of PTY" , "bool" },
381423 }
382424
383425 for _ , spec := range flagSpecs {
0 commit comments