mirror of
https://github.com/coder/coder.git
synced 2026-06-07 23:18:20 +00:00
e57525002c
Remove the `ExperimentAgents` feature flag so the Agents feature is always available without requiring `--experiments=agents`. The feature is now in beta. Existing deployments that still pass `--experiments=agents` will get a harmless "ignoring unknown experiment" warning on startup. ### Changes **Backend:** - Remove `RequireExperimentWithDevBypass` middleware from chat and MCP server routes - Always include `AgentsAccessRole` in assignable site roles (later refactored to org-scoped on main; rebase keeps that) - Always set `AgentsTabVisible = true`, then drop the entire dead `AgentsTabVisible` metadata pipeline (Go htmlState field, populateHTMLState goroutine, HTML meta tag, useEmbeddedMetadata registration, mock); no production consumer reads it. `AgentsNavItem` already gates on `permissions.createChat`. - Make `blob:` CSP `img-src` addition unconditional - Remove `ExperimentAgents` constant, `DisplayName` case, and `ExperimentsKnown` entry **CLI:** - Graduate the agents TUI from `coder exp agents` to `coder agents` (moved from `AGPLExperimental()` to `CoreSubcommands()`) - Drop the `agent` alias so it does not collide with the hidden workspace-agent command - Rename implementation files `cli/exp_agents_*.go` -> `cli/agents_*.go` and internal identifiers (`expChatsTUIModel` -> `chatsTUIModel`, `newExpChatsTUIModel` -> `newChatsTUIModel`, `setupExpAgentsBackend` -> `setupAgentsBackend`, `startExpAgentsSession` -> `startAgentsSession`, `expAgentsPtr` -> `agentsPtr`, `expAgentsSession` -> `agentsSession`, `TestExpAgents*` -> `TestAgents*`). `expClient` (the `*codersdk.ExperimentalClient` local) is kept; `coderd/exp_chats*.go` and other still-experimental `cli/exp_*.go` commands are intentionally untouched. **Frontend:** - Remove experiment check from `AgentsNavItem` - render when `canCreateChat` is true - Remove `agentsEnabled` experiment check from `WorkspacesPage`, then gate `chatsByWorkspace` on `permissions.createChat` so users without chat access don't trigger the per-page DB query (Copilot review feedback) - Add `FeatureStageBadge` (beta) next to the Coder logo in the Agents sidebar (desktop + mobile) **Docs:** - Remove experiment flag setup instructions from `early-access.md` and `getting-started.md` (and rename `early-access.md`'s "Enable Coder Agents" heading to "Set up Coder Agents", since there is no enablement step left) - Update `chats-api.md` and `getting-started.md`'s Chats API note to say "beta" instead of "experimental" - `docs/manifest.json`: drop "experimental" from the Chats API sidebar description - `make gen` regenerated `docs/reference/cli/agents.md` and the CLI index - `scripts/check_emdash.sh`: exclude `cli/testdata/*.golden` and `enterprise/cli/testdata/*.golden` from the new repo-wide emdash lint, since serpent emits emdash borders in every generated `--help` golden file **Tests:** - Remove `ExperimentAgents` setup from all test files (14 occurrences across 7 files) - Update stale "with the agents experiment" comments in `coderd/x/chatd/integration_test.go` and `coderd/mcp_test.go` <img width="1185" height="900" alt="image" src="https://github.com/user-attachments/assets/b420bc8f-41d6-42c6-abd8-ad572533d651" /> > 🤖 Generated by Coder Agents
99 lines
3.4 KiB
Go
99 lines
3.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
type tuiStyles struct {
|
|
title lipgloss.Style
|
|
subtitle lipgloss.Style
|
|
statusBar lipgloss.Style
|
|
statusBadge lipgloss.Style
|
|
selectedItem lipgloss.Style
|
|
selectedBlock lipgloss.Style
|
|
normalItem lipgloss.Style
|
|
dimmedText lipgloss.Style
|
|
errorText lipgloss.Style
|
|
searchInput lipgloss.Style
|
|
separator lipgloss.Style
|
|
helpText lipgloss.Style
|
|
modeBadgeExec lipgloss.Style
|
|
modeBadgePlan lipgloss.Style
|
|
userMessage lipgloss.Style
|
|
assistantMsg lipgloss.Style
|
|
reasoning lipgloss.Style
|
|
toolCallStyle lipgloss.Style
|
|
toolPending lipgloss.Style
|
|
toolSuccess lipgloss.Style
|
|
compaction lipgloss.Style
|
|
warningText lipgloss.Style
|
|
criticalText lipgloss.Style
|
|
overlayBorder lipgloss.Style
|
|
composerStyle lipgloss.Style
|
|
}
|
|
|
|
func newTUIStyles(renderers ...*lipgloss.Renderer) tuiStyles {
|
|
renderer := lipgloss.DefaultRenderer()
|
|
if len(renderers) > 0 && renderers[0] != nil {
|
|
renderer = renderers[0]
|
|
}
|
|
|
|
return tuiStyles{
|
|
title: renderer.NewStyle().Bold(true),
|
|
subtitle: renderer.NewStyle().Faint(true),
|
|
statusBar: renderer.NewStyle(),
|
|
statusBadge: renderer.NewStyle().Padding(0, 1),
|
|
selectedItem: renderer.NewStyle().Bold(true),
|
|
selectedBlock: renderer.NewStyle().
|
|
BorderLeft(true).
|
|
BorderStyle(lipgloss.NormalBorder()).
|
|
BorderForeground(lipgloss.AdaptiveColor{Light: "63", Dark: "63"}).
|
|
PaddingLeft(1),
|
|
normalItem: renderer.NewStyle(),
|
|
dimmedText: renderer.NewStyle().Faint(true),
|
|
errorText: renderer.NewStyle().Foreground(lipgloss.Color("1")),
|
|
searchInput: renderer.NewStyle().
|
|
BorderStyle(lipgloss.NormalBorder()).
|
|
BorderBottom(true),
|
|
separator: renderer.NewStyle().Faint(true),
|
|
helpText: renderer.NewStyle().Faint(true),
|
|
modeBadgeExec: renderer.NewStyle().Bold(true).Foreground(lipgloss.AdaptiveColor{Light: "22", Dark: "42"}),
|
|
modeBadgePlan: renderer.NewStyle().Bold(true).Foreground(lipgloss.AdaptiveColor{Light: "130", Dark: "214"}),
|
|
userMessage: renderer.NewStyle().Bold(true).Foreground(lipgloss.Color("6")),
|
|
assistantMsg: renderer.NewStyle(),
|
|
reasoning: renderer.NewStyle().Faint(true).Italic(true),
|
|
toolCallStyle: renderer.NewStyle().Foreground(lipgloss.Color("3")),
|
|
toolPending: renderer.NewStyle().Faint(true).Foreground(lipgloss.Color("3")),
|
|
toolSuccess: renderer.NewStyle().Foreground(lipgloss.Color("2")),
|
|
compaction: renderer.NewStyle().Bold(true).Foreground(lipgloss.Color("5")),
|
|
warningText: renderer.NewStyle().Foreground(lipgloss.Color("3")),
|
|
criticalText: renderer.NewStyle().Foreground(lipgloss.Color("1")).Bold(true),
|
|
overlayBorder: renderer.NewStyle().BorderStyle(lipgloss.RoundedBorder()).Padding(1),
|
|
composerStyle: renderer.NewStyle().BorderStyle(lipgloss.NormalBorder()).BorderTop(true),
|
|
}
|
|
}
|
|
|
|
func (s tuiStyles) statusColor(status codersdk.ChatStatus) lipgloss.Style {
|
|
color := lipgloss.Color("7")
|
|
switch status {
|
|
case codersdk.ChatStatusWaiting, codersdk.ChatStatusPending:
|
|
color = lipgloss.Color("3")
|
|
case codersdk.ChatStatusRunning:
|
|
color = lipgloss.Color("4")
|
|
case codersdk.ChatStatusPaused:
|
|
color = lipgloss.Color("5")
|
|
case codersdk.ChatStatusCompleted:
|
|
color = lipgloss.Color("2")
|
|
case codersdk.ChatStatusError:
|
|
color = lipgloss.Color("1")
|
|
}
|
|
return s.statusBadge.Foreground(color)
|
|
}
|
|
|
|
func (s tuiStyles) truncate(text string, maxWidth int) string {
|
|
_ = s
|
|
return truncateText(text, maxWidth, "", 3)
|
|
}
|