1// Package acp implements the Agent-Client Protocol server for Crush.
2//
3// ACP allows external clients (web, desktop, mobile) to drive Crush as an
4// agent server over stdio using JSON-RPC.
5package acp
6
7import (
8 "context"
9 "log/slog"
10 "os"
11 "os/signal"
12 "syscall"
13
14 "github.com/coder/acp-go-sdk"
15)
16
17// Server manages the ACP connection lifecycle.
18type Server struct {
19 ctx context.Context
20 cancel context.CancelFunc
21 agent *Agent
22}
23
24// NewServer creates a new ACP server.
25func NewServer(ctx context.Context) *Server {
26 ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
27 return &Server{
28 ctx: ctx,
29 cancel: cancel,
30 }
31}
32
33// Run starts the ACP server and blocks until the connection closes.
34func (s *Server) Run(agent *Agent) error {
35 s.agent = agent
36 slog.Info("Starting ACP server")
37
38 conn := acp.NewAgentSideConnection(agent, os.Stdout, os.Stdin)
39 conn.SetLogger(slog.Default())
40 agent.SetAgentConnection(conn)
41
42 select {
43 case <-conn.Done():
44 slog.Debug("ACP client disconnected")
45 case <-s.ctx.Done():
46 slog.Debug("ACP server received shutdown signal")
47 }
48
49 return nil
50}
51
52// Shutdown performs graceful shutdown.
53func (s *Server) Shutdown() {
54 s.cancel()
55}