web

package
v1.9.30 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: MIT Imports: 37 Imported by: 0

Documentation

Overview

POST /api/sessions/{id}/worktree/finish — Web parity for the TUI's W/shift+w hotkey and the `agent-deck worktree finish` CLI. Closes the "Finish worktree" MISSING row in tests/web/PARITY_MATRIX.md (issue #1126).

Index

Constants

View Source
const (
	ErrCodeUnauthorized     = "UNAUTHORIZED"
	ErrCodeForbidden        = "MUTATIONS_DISABLED"
	ErrCodeNotFound         = "NOT_FOUND"
	ErrCodeBadRequest       = "INVALID_REQUEST"
	ErrCodeMethodNotAllowed = "METHOD_NOT_ALLOWED"
	ErrCodeRateLimited      = "RATE_LIMITED"
	ErrCodeInternalError    = "INTERNAL_ERROR"
	ErrCodeNotImplemented   = "NOT_IMPLEMENTED"
	ErrCodeReadOnly         = "READ_ONLY"
)

Error code constants for API error responses.

View Source
const (
	MenuItemTypeGroup   = "group"
	MenuItemTypeSession = "session"
)
View Source
const DefaultUndoWindow = 30 * time.Second

DefaultUndoWindow is the default Chrome-style undo grace period for session deletes (POST /api/sessions/undelete). Mirrors the TUI ctrl+z in-memory undo stack window.

Variables

View Source
var ErrNotAWorktree = errors.New("session is not in a worktree")

ErrNotAWorktree is returned by SessionMutator.FinishWorktree when the target session exists but is not in a git/jujutsu worktree (so there is nothing to merge or clean up). The handler maps this to 400. See issue #1126.

View Source
var ErrSessionNotFound = errors.New("session not found")

ErrSessionNotFound is returned by SessionMutator.FinishWorktree when the target session id does not resolve to a live instance. The handler maps this to 404. See issue #1126.

View Source
var ErrTmuxSessionNotFound = errors.New("tmux session not found")
View Source
var ErrUndoExpired = errors.New("undo window expired")

ErrUndoExpired is returned by SessionMutator.UndoDelete when the most recent delete is older than the configured undo window.

View Source
var ErrUndoNothing = errors.New("nothing to undo")

ErrUndoNothing is returned by SessionMutator.UndoDelete when the undo stack is empty.

Functions

func EnsurePushVAPIDKeys

func EnsurePushVAPIDKeys(profile, subject string) (publicKey, privateKey string, generated bool, err error)

EnsurePushVAPIDKeys returns a persisted VAPID keypair for the given profile. If no key file exists yet, it generates one via webpush.GenerateVAPIDKeys().

Types

type Assets added in v1.5.0

type Assets struct {
	// contains filtered or unexported fields
}

Assets manages the manifest-based mapping from logical source paths (e.g. "app/main.js") to hashed bundle output paths (e.g. "dist/main.a1b2c3.js"). In dev mode (no manifest present) or when the AGENTDECK_WEB_BUNDLE=0 env var is set, ResolveAsset falls back to serving the unbundled source file at /static/<logical>.

PERF-H: the manifest is emitted by bundle.go (go:generate entry point) which drives github.com/evanw/esbuild/pkg/api. The dev/prod fork keeps live-reload friendly while shipping hashed, bundled JS in production.

func LoadAssets added in v1.5.0

func LoadAssets(manifestPath string) (*Assets, error)

LoadAssets reads a manifest file from a filesystem path and returns an Assets instance. A missing manifest is NOT an error — the returned instance operates in dev mode (fallback to /static/<logical>). The AGENTDECK_WEB_BUNDLE=0 env var forces dev mode even if the manifest is present, which is the rollback lever.

func LoadAssetsFromFS added in v1.5.0

func LoadAssetsFromFS(fsys fs.FS, name string) (*Assets, error)

LoadAssetsFromFS reads a manifest from an io/fs.FS (e.g. the server's embed.FS). Used by the server at startup so the manifest can travel inside the binary. Missing file → dev mode.

func (*Assets) ResolveAsset added in v1.5.0

func (a *Assets) ResolveAsset(logical string) string

ResolveAsset maps a logical source path to the URL the browser should fetch. In dev mode: "/static/<logical>". In prod mode: "/static/<hashed>". Unknown logical paths fall back to the logical form so missing-asset regressions surface as 404s rather than silent manifest lookups.

func (*Assets) SubstitutePlaceholders added in v1.5.0

func (a *Assets) SubstitutePlaceholders(template string) string

SubstitutePlaceholders replaces every {{ASSET:logical}} token in the input string with the resolved URL. Used to fill index.html at serve time so the on-disk file stays hand-written (Pitfall 3 mitigation: dirty-tree check remains clean because the bundler never writes back to index.html).

type Config

type Config struct {
	ListenAddr          string
	Profile             string
	ReadOnly            bool
	WebMutations        bool // When false, POST/PATCH/DELETE endpoints return 403
	Token               string
	MenuData            MenuDataLoader
	PushVAPIDPublicKey  string
	PushVAPIDPrivateKey string
	PushVAPIDSubject    string
	PushTestInterval    time.Duration
}

Config defines runtime options for the web server.

type CreateGroupRequest added in v1.3.4

type CreateGroupRequest struct {
	Name       string `json:"name"`
	ParentPath string `json:"parentPath,omitempty"`
}

CreateGroupRequest is the body for POST /api/groups.

type CreateSessionRequest added in v1.3.4

type CreateSessionRequest struct {
	Title       string `json:"title"`
	Tool        string `json:"tool"`
	ProjectPath string `json:"projectPath"`
	GroupPath   string `json:"groupPath,omitempty"`
	ModelID     string `json:"modelId,omitempty"`
}

CreateSessionRequest is the body for POST /api/sessions.

type MCPCatalogEntry added in v1.9.25

type MCPCatalogEntry struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Transport   string `json:"transport,omitempty"`
	Command     string `json:"command,omitempty"`
	URL         string `json:"url,omitempty"`
}

MCPCatalogEntry describes one MCP available in the catalog (config.toml).

type MCPCatalogResponse added in v1.9.25

type MCPCatalogResponse struct {
	MCPs []MCPCatalogEntry `json:"mcps"`
}

MCPCatalogResponse is returned by GET /api/mcps.

type MCPManager added in v1.9.25

type MCPManager interface {
	ListCatalog() []MCPCatalogEntry
	ListAttached(projectPath string) (map[string][]string, error)
	Attach(projectPath, name, scope string) error
	Detach(projectPath, name, scope string) error
	Move(projectPath, name, fromScope, toScope string) error
}

MCPManager is the seam between web HTTP handlers and the on-disk MCP catalog + scope-specific config files. Tests inject a fake; production gets defaultMCPManager which delegates to internal/session.

func NewDefaultMCPManager added in v1.9.25

func NewDefaultMCPManager() MCPManager

NewDefaultMCPManager returns the production MCPManager that reads/writes real config files via internal/session helpers.

type MemoryMenuData

type MemoryMenuData struct {
	// contains filtered or unexported fields
}

MemoryMenuData is an in-memory menu snapshot store used by web mode. It can optionally fall back to a loader (e.g. storage-backed) until the first in-memory snapshot is published.

func NewMemoryMenuData

func NewMemoryMenuData(fallback MenuDataLoader) *MemoryMenuData

NewMemoryMenuData creates an in-memory menu data store.

func (*MemoryMenuData) LoadMenuSnapshot

func (m *MemoryMenuData) LoadMenuSnapshot() (*MenuSnapshot, error)

LoadMenuSnapshot returns the latest in-memory snapshot. If no snapshot exists yet, it falls back once to the configured loader.

func (*MemoryMenuData) SetSnapshot

func (m *MemoryMenuData) SetSnapshot(snapshot *MenuSnapshot)

SetSnapshot replaces the stored menu snapshot.

func (*MemoryMenuData) UpdateSessionStates

func (m *MemoryMenuData) UpdateSessionStates(states map[string]MenuSessionState, generatedAt time.Time)

UpdateSessionStates updates status/tool fields in-place for existing sessions.

type MenuDataLoader interface {
	LoadMenuSnapshot() (*MenuSnapshot, error)
}

MenuDataLoader provides menu snapshots for web APIs and push notifications.

type MenuGroup struct {
	Name         string `json:"name"`
	Path         string `json:"path"`
	Expanded     bool   `json:"expanded"`
	Order        int    `json:"order"`
	SessionCount int    `json:"sessionCount"`
}

MenuGroup contains metadata for a group item.

type MenuItem struct {
	Index               int          `json:"index"`
	Type                string       `json:"type"`
	Level               int          `json:"level"`
	Path                string       `json:"path,omitempty"`
	Group               *MenuGroup   `json:"group,omitempty"`
	Session             *MenuSession `json:"session,omitempty"`
	IsLastInGroup       bool         `json:"isLastInGroup,omitempty"`
	IsSubSession        bool         `json:"isSubSession,omitempty"`
	IsLastSubSession    bool         `json:"isLastSubSession,omitempty"`
	ParentIsLastInGroup bool         `json:"parentIsLastInGroup,omitempty"`
}

MenuItem represents one row in the flattened navigation list.

type MenuSession struct {
	ID              string         `json:"id"`
	Title           string         `json:"title"`
	Tool            string         `json:"tool"`
	ModelID         string         `json:"modelId,omitempty"`
	Model           string         `json:"model,omitempty"`
	ModelVersion    string         `json:"modelVersion,omitempty"`
	Status          session.Status `json:"status"`
	GroupPath       string         `json:"groupPath"`
	ProjectPath     string         `json:"projectPath"`
	ParentSessionID string         `json:"parentSessionId,omitempty"`
	Order           int            `json:"order"`
	TmuxSession     string         `json:"tmuxSession,omitempty"`
	// TmuxSocketName is the tmux -L selector captured at session creation
	// (Instance.TmuxSocketName). Surfaced so the web PTY bridge can reach
	// sessions running on an isolated socket (issue #687, v1.7.50).
	TmuxSocketName string    `json:"tmuxSocketName,omitempty"`
	CreatedAt      time.Time `json:"createdAt"`
	LastAccessedAt time.Time `json:"lastAccessedAt,omitempty"`

	IsConductor bool `json:"isConductor,omitempty"`

	ClaudeSessionID   string `json:"claudeSessionId,omitempty"`
	GeminiSessionID   string `json:"geminiSessionId,omitempty"`
	GeminiModel       string `json:"geminiModel,omitempty"`
	GeminiYoloMode    *bool  `json:"geminiYoloMode,omitempty"`
	CodexSessionID    string `json:"codexSessionId,omitempty"`
	OpenCodeSessionID string `json:"opencodeSessionId,omitempty"`

	LatestPrompt string `json:"latestPrompt,omitempty"`
	Notes        string `json:"notes,omitempty"`

	Color string `json:"color,omitempty"`

	Command         string          `json:"command,omitempty"`
	Wrapper         string          `json:"wrapper,omitempty"`
	Channels        []string        `json:"channels,omitempty"`
	ExtraArgs       []string        `json:"extraArgs,omitempty"`
	ToolOptionsJSON json.RawMessage `json:"toolOptions,omitempty"`

	Sandbox          *session.SandboxConfig `json:"sandbox,omitempty"`
	SandboxContainer string                 `json:"sandboxContainer,omitempty"`
	SSHHost          string                 `json:"sshHost,omitempty"`
	SSHRemotePath    string                 `json:"sshRemotePath,omitempty"`

	MultiRepoEnabled   bool                        `json:"multiRepoEnabled,omitempty"`
	AdditionalPaths    []string                    `json:"additionalPaths,omitempty"`
	MultiRepoTempDir   string                      `json:"multiRepoTempDir,omitempty"`
	MultiRepoWorktrees []session.MultiRepoWorktree `json:"multiRepoWorktrees,omitempty"`

	WorktreePath     string `json:"worktreePath,omitempty"`
	WorktreeRepoRoot string `json:"worktreeRepoRoot,omitempty"`
	WorktreeBranch   string `json:"worktreeBranch,omitempty"`

	TitleLocked        bool `json:"titleLocked,omitempty"`
	NoTransitionNotify bool `json:"noTransitionNotify,omitempty"`

	LoadedMCPNames []string `json:"loadedMcpNames,omitempty"`

	// claude_analytics has no underlying struct on *Instance so the matrix
	// keeps it MISSING; only gemini is exposed today.
	GeminiAnalytics *session.GeminiSessionAnalytics `json:"geminiAnalytics,omitempty"`
}

MenuSession contains metadata for a session item.

type MenuSessionState struct {
	Status session.Status
	Tool   string
}

MenuSessionState is a lightweight status/tool update for one session.

type MenuSnapshot struct {
	Profile       string     `json:"profile"`
	GeneratedAt   time.Time  `json:"generatedAt"`
	TotalGroups   int        `json:"totalGroups"`
	TotalSessions int        `json:"totalSessions"`
	Items         []MenuItem `json:"items"`
}

MenuSnapshot is a flattened, ordered representation of session navigation data.

func BuildMenuSnapshot

func BuildMenuSnapshot(profile string, instances []*session.Instance, groupsData []*session.GroupData, generatedAt time.Time) *MenuSnapshot

BuildMenuSnapshot converts in-memory session/group state into a flattened web DTO.

type ProfilesResponse added in v1.3.4

type ProfilesResponse struct {
	Current  string   `json:"current"`
	Profiles []string `json:"profiles"`
}

ProfilesResponse is returned by GET /api/profiles.

type RenameGroupRequest added in v1.3.4

type RenameGroupRequest struct {
	Name string `json:"name"`
}

RenameGroupRequest is the body for PATCH /api/groups/:path.

type SSECostEvent added in v1.3.4

type SSECostEvent struct {
	EventType string  `json:"eventType"`
	SessionID string  `json:"sessionId"`
	Cost      float64 `json:"cost"`
}

SSECostEvent is emitted on cost:updated events.

type SSEDeleteEvent added in v1.3.4

type SSEDeleteEvent struct {
	EventType string `json:"eventType"`
	ID        string `json:"id"`
}

SSEDeleteEvent is emitted on session:deleted events.

type SSEGroupDeleteEvent added in v1.3.4

type SSEGroupDeleteEvent struct {
	EventType string `json:"eventType"`
	Path      string `json:"path"`
}

SSEGroupDeleteEvent is emitted on group:deleted events.

type SSEGroupEvent added in v1.3.4

type SSEGroupEvent struct {
	EventType string     `json:"eventType"`
	Group     *MenuGroup `json:"group"`
}

SSEGroupEvent is emitted on group:created and group:updated events.

type SSESessionEvent added in v1.3.4

type SSESessionEvent struct {
	EventType string       `json:"eventType"`
	Session   *MenuSession `json:"session"`
}

SSESessionEvent is emitted on session:created and session:updated events.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server wraps an HTTP server for Agent Deck web mode.

func NewServer

func NewServer(cfg Config) *Server

NewServer creates a new web server with base routes and middleware.

func (*Server) Addr

func (s *Server) Addr() string

Addr returns the listen address.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the configured HTTP handler (used by tests).

func (*Server) HasMCPManager added in v1.9.25

func (s *Server) HasMCPManager() bool

HasMCPManager reports whether the MCP manager seam is wired.

func (*Server) HasMutator added in v1.7.75

func (s *Server) HasMutator() bool

HasMutator reports whether a SessionMutator has been wired. Mutating endpoints (POST/PATCH/DELETE) return 503 NOT_IMPLEMENTED when this is false, even if WebMutations is true. Exposed for regression tests on the `agent-deck web` bootstrap path.

func (*Server) SetCostStore added in v0.26.4

func (s *Server) SetCostStore(store *costs.Store)

func (*Server) SetMCPManager added in v1.9.25

func (s *Server) SetMCPManager(m MCPManager)

SetMCPManager wires the MCP manager implementation (production or test).

func (*Server) SetMutator added in v1.3.4

func (s *Server) SetMutator(m SessionMutator)

SetMutator injects the session mutator implementation (typically *ui.WebMutator).

func (*Server) SetSkillsService added in v1.9.25

func (s *Server) SetSkillsService(svc SkillsService)

SetSkillsService injects an alternate SkillsService (used by tests). When nil, handlers fall back to defaultSkillsService.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully stops the server.

func (*Server) Start

func (s *Server) Start() error

Start starts the HTTP server and blocks until shutdown or error. Returns nil on graceful shutdown.

func (*Server) String

func (s *Server) String() string

type SessionActionResponse added in v1.3.4

type SessionActionResponse struct {
	SessionID string         `json:"sessionId"`
	Status    session.Status `json:"status"`
}

SessionActionResponse is returned by session action endpoints.

type SessionChildNode added in v1.9.26

type SessionChildNode struct {
	*MenuSession
	Children []*SessionChildNode `json:"children"`
}

SessionChildNode is one node in the children-tree response. It inlines the standard MenuSession fields and adds a recursive `children` array. `children` is always non-nil — even leaves render as `"children":[]` so JS consumers don't have to null-check.

type SessionChildrenResponse added in v1.9.26

type SessionChildrenResponse struct {
	SessionID string              `json:"sessionId"`
	Children  []*SessionChildNode `json:"children"`
}

SessionChildrenResponse is the body of GET /api/sessions/{id}/children.

type SessionDataService

type SessionDataService struct {
	// contains filtered or unexported fields
}

SessionDataService loads profile session data and transforms it into web-friendly DTOs.

func NewSessionDataService

func NewSessionDataService(profile string) *SessionDataService

NewSessionDataService creates a SessionDataService for a profile.

func (*SessionDataService) LoadMenuSnapshot

func (s *SessionDataService) LoadMenuSnapshot() (*MenuSnapshot, error)

LoadMenuSnapshot loads sessions/groups and returns a deterministic flattened menu DTO.

func (*SessionDataService) Profile

func (s *SessionDataService) Profile() string

Profile returns the effective profile this service reads from.

type SessionMCPsResponse added in v1.9.25

type SessionMCPsResponse struct {
	SessionID string   `json:"sessionId"`
	Local     []string `json:"local"`
	Global    []string `json:"global"`
	User      []string `json:"user"`
}

SessionMCPsResponse is returned by GET /api/sessions/{id}/mcps.

type SessionMutator added in v1.3.4

type SessionMutator interface {
	CreateSession(title, tool, projectPath, groupPath, modelID string) (string, error)
	StartSession(sessionID string) error
	StopSession(sessionID string) error
	RestartSession(sessionID string) error
	DeleteSession(sessionID string) error
	// CloseSession stops the session process while keeping its metadata
	// in storage (TUI Shift+D — non-destructive close).
	CloseSession(sessionID string) error
	ForkSession(sessionID string) (string, error)
	// UndoDelete restores the most-recently deleted session if it was
	// deleted within the implementation's undo window. Returns the
	// restored session id. Implementations should return ErrUndoNothing
	// when the stack is empty and ErrUndoExpired when the most recent
	// entry is older than the window — the handler maps both to 404.
	UndoDelete() (string, error)
	CreateGroup(name, parentPath string) (string, error)
	RenameGroup(groupPath, newName string) error
	DeleteGroup(groupPath string) error
	// FinishWorktree merges (or skips), removes the worktree, optionally
	// deletes the source branch, kills the tmux session, and removes the
	// session from storage. Mirrors the TUI W/shift+w hotkey and the
	// `agent-deck worktree finish` CLI. Returns ErrSessionNotFound when
	// the id doesn't resolve and ErrNotAWorktree when the session exists
	// but lacks worktree metadata. See issue #1126.
	FinishWorktree(sessionID string, opts WorktreeFinishOptions) (WorktreeFinishResult, error)
}

SessionMutator is implemented by internal/ui.WebMutator and injected at startup. It bridges web HTTP handlers to the TUI session/group management methods.

type SettingsResponse added in v1.3.4

type SettingsResponse struct {
	Profile      string `json:"profile"`
	ReadOnly     bool   `json:"readOnly"`
	WebMutations bool   `json:"webMutations"`
	Version      string `json:"version"`
}

SettingsResponse is returned by GET /api/settings.

type SkillsService added in v1.9.25

type SkillsService interface {
	ListCatalog() ([]session.SkillCandidate, error)
	ListAttached(projectPath string) ([]session.ProjectSkillAttachment, error)
	Attach(projectPath, tool, skillRef, source string) (*session.ProjectSkillAttachment, error)
	Detach(projectPath, skillRef, source string) (*session.ProjectSkillAttachment, error)
}

SkillsService is the seam between web HTTP handlers and the on-disk skill catalog/attachment functions in internal/session. Tests inject a fake; production gets defaultSkillsService which delegates straight to the session package.

type WorktreeFinishOptions added in v1.9.30

type WorktreeFinishOptions struct {
	// Into is the target branch to merge the worktree branch into. When
	// empty the backend's default branch is used (matches the
	// `agent-deck worktree finish --into` flag).
	Into string
	// NoMerge skips the merge step (mirrors --no-merge). The branch is
	// still removed (unless KeepBranch) and the worktree torn down.
	NoMerge bool
	// KeepBranch leaves the source branch in place after finishing
	// (mirrors --keep-branch). Useful when the branch already lives on a
	// remote PR.
	KeepBranch bool
	// Force skips the dirty-worktree safety check and forces branch
	// deletion even if the merge fast-forward didn't succeed.
	Force bool
}

WorktreeFinishOptions configures a SessionMutator.FinishWorktree call. All fields are optional; the zero value asks the implementation to auto-detect the target branch, perform the merge, delete the source branch, and refuse if the worktree is dirty.

type WorktreeFinishRequest added in v1.9.30

type WorktreeFinishRequest struct {
	Into       string `json:"into,omitempty"`
	NoMerge    bool   `json:"noMerge,omitempty"`
	KeepBranch bool   `json:"keepBranch,omitempty"`
	Force      bool   `json:"force,omitempty"`
}

WorktreeFinishRequest is the body for POST /api/sessions/{id}/worktree/finish. All fields are optional. Mirrors `agent-deck worktree finish` CLI flags. See issue #1126.

type WorktreeFinishResponse added in v1.9.30

type WorktreeFinishResponse struct {
	SessionID     string `json:"sessionId"`
	Branch        string `json:"branch"`
	MergedInto    string `json:"mergedInto,omitempty"`
	Merged        bool   `json:"merged"`
	BranchDeleted bool   `json:"branchDeleted"`
}

WorktreeFinishResponse is returned by POST /api/sessions/{id}/worktree/finish.

type WorktreeFinishResult added in v1.9.30

type WorktreeFinishResult struct {
	SessionID     string
	Branch        string
	MergedInto    string
	Merged        bool
	BranchDeleted bool
}

WorktreeFinishResult is what SessionMutator.FinishWorktree returns on success. Mirrors the JSON-output payload of `agent-deck worktree finish --json`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL