Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133package app
import ( "albedo/cli/internal/daemon" "albedo/cli/internal/daemon/protocol" "context" "encoding/json" "slices")
// ACPConnection owns HTTP workflows; the protocol controller owns observation policy.type ACPConnection struct { connection *daemon.Connection token string}
type ACPModels struct { Current string Available []string}
func (a *ACPConnection) Models(ctx context.Context) (ACPModels, error) { profiles, err := daemon.ProviderProfiles(ctx, a.connection) if err != nil { return ACPModels{}, err } models := ACPModels{} for _, catalog := range configuredModels(ctx, a.connection, profiles) { for _, model := range catalog.models { models.Available = append(models.Available, catalog.provider+"/"+model) } } return models, nil}
func (models ACPModels) WithCurrent(current string) ACPModels { models.Current = current if !slices.Contains(models.Available, current) { models.Available = append(models.Available, current) } return models}
func (a *ACPConnection) CurrentModel(ctx context.Context, id string) (string, error) { session, err := daemon.GetSession(ctx, a.connection, id) return session.Provider + "/" + session.Model, err}
// SetModel returns the selected model; nil Available keeps the adapter's cached choices.func (a *ACPConnection) SetModel(ctx context.Context, id, value string) (ACPModels, error) { profiles, err := daemon.ProviderProfiles(ctx, a.connection) if err != nil { return ACPModels{}, err } provider, model, err := daemon.ResolveSessionModel(profiles, value) if err != nil { return ACPModels{}, &daemon.APIError{StatusCode: 400, Code: "model_unavailable", Message: err.Error()} } if provider+"/"+model != value { return ACPModels{}, &daemon.APIError{StatusCode: 400, Code: "model_unavailable", Message: "The selected provider is no longer configured; resume to refresh models"} } if err = switchModel(ctx, a.connection, id, modelChoice{provider, model}, a.token); err != nil { return ACPModels{}, err } models, err := a.Models(ctx) if err != nil { return ACPModels{Current: value}, nil } return models.WithCurrent(value), nil}
func (s *Service) ConnectACP(ctx context.Context, token string) (*ACPConnection, error) { conn, err := s.Connect(ctx) if err != nil { return nil, err } info, err := daemon.ProbeServer(ctx, conn) if err != nil { return nil, err } if info.Capabilities["acp_attachment"] < 3 { return nil, &daemon.APIError{Code: "daemon_incompatible", Message: "Run `albedo daemon --restart` in a terminal to enable ACP."} } return &ACPConnection{connection: conn, token: token}, nil}func (a *ACPConnection) New(ctx context.Context, cwd string, servers []protocol.ACPMCPServer) (protocol.ACPAttachment, error) { profiles, err := daemon.ProviderProfiles(ctx, a.connection) if err != nil { return protocol.ACPAttachment{}, err } _, configured := profiles.Providers[profiles.Active] if profiles.Active == "" || !configured { return protocol.ACPAttachment{}, &daemon.APIError{Code: "provider_missing", Message: "Run `albedo login` in a terminal, then try again."} } id, err := daemon.CreateACPSession(ctx, a.connection, a.token, protocol.ACPSessionRequest{Kind: "acp", Workspace: cwd, MCPServers: servers}) if err != nil { return protocol.ACPAttachment{}, err } return a.Resume(ctx, id, cwd, servers)}func (a *ACPConnection) Resume(ctx context.Context, id, cwd string, servers []protocol.ACPMCPServer) (protocol.ACPAttachment, error) { return daemon.AttachACP(ctx, a.connection, id, a.token, protocol.ACPAttachRequest{Workspace: cwd, MCPServers: servers})}func (a *ACPConnection) Prepare(id string, blocks []json.RawMessage) (*daemon.OperationHandle, error) { return daemon.NewACPInput(id, a.token, blocks)}func (a *ACPConnection) Submit(ctx context.Context, handle *daemon.OperationHandle) error { _, err := daemon.SubmitOperation(ctx, a.connection, handle) return err}func (a *ACPConnection) Renew(ctx context.Context, id string) error { return daemon.RenewACP(ctx, a.connection, id, a.token)}func (a *ACPConnection) Release(ctx context.Context, id string) error { return daemon.ReleaseACP(ctx, a.connection, id, a.token)}func (a *ACPConnection) Observer(id string, cursor protocol.Cursor) *daemon.ChatClient { client := daemon.NewChatClient(a.connection, id) client.SeedCursor(cursor) return client}
func (a *ACPConnection) Cancel(ctx context.Context, session, id string) (protocol.InputCancellation, error) { return daemon.NewChatClient(a.connection, session).CancelInput(ctx, id, a.token)}
func (a *ACPConnection) List(ctx context.Context, cwd, cursor *string) (protocol.SessionPage, error) { return daemon.ListACPSessions(ctx, a.connection, cwd, cursor)}func (a *ACPConnection) History(ctx context.Context, id string, cursor daemon.ACPHistoryCursor, cutoff int64) ([]daemon.StreamEvent, daemon.ACPHistoryCursor, error) { return daemon.ACPHistory(ctx, a.connection, id, cursor, cutoff)}