Files
kandev/apps/backend/internal/agent/settings/dto/dto.go
T
João Salavisa a7005e5d2f chore: revert agent subscription usage in settings and chat tooltip (#1940)
* chore: revert agent subscription usage in settings and chat tooltip

Reverts #1697 (55c8b461b). The subscription usage plugin now covers this,
so the built-in Settings > Agents section and the context-doughnut tooltip
rows are no longer needed.

Conflicts resolved to preserve later work on the same files:
- token-usage-display.tsx/.test.tsx: kept the pinnable tooltip (#1869),
  the rebuilt context-window layout (#1741), and the ACP/API source row
  (#1752); removed only SessionUsageRows and its imports.
- settings/agents/page.tsx: #1741 moved <AgentUsageSection /> above the
  pre-existing <Separator />, so the revert missed the JSX; removed it
  and kept the separator.
- agent-usage-section.tsx/.test.tsx: deleted (later changes were cosmetic
  tweaks to the section itself).

Office-side subscription surfaces (dashboard quota card, agent detail
QuotaCard, internal/agent/usage clients) predate #1697 and are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(usage): keep #1697 client fixes when reverting its UI surface

#1697 bundled two things: the Settings > Agents section plus doughnut
tooltip rows, and a set of genuine bug fixes to the underlying provider
usage clients. Reverting it wholesale threw away the second group, which
is still live code behind the office dashboard quota card and the agent
detail QuotaCard.

Restores the client layer to its post-#1697 state (billing.go, cache.go,
client_claude.go, client_codex.go, fileutil.go, types.go and the two
client tests), fixing the defects CodeRabbit flagged on #1940:

- billing type is recomputed per call again instead of being frozen for
  the process lifetime by sync.OnceValue, so login/logout is observed
  without a backend restart
- usage cache regains fetch coalescing and short negative caching, so a
  provider outage no longer means one outbound call per request
- Claude usage parses float utilization and the richer limits[] array
  instead of failing to decode
- Claude token refresh patches only claudeAiOauth in a decoded map and
  writes atomically via temp+rename, preserving unknown keys in the
  credentials file the real CLI shares
- Codex reads tokens.access_token rather than a nonexistent flat token
  field, and its doc comment matches the ~/.codex/auth.json path

The removed UI/API surface stays reverted: GET /api/v1/agents/usage, the
host usage lister, the settings section, and the tooltip rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 12:04:25 +01:00

300 lines
13 KiB
Go

package dto
import (
"time"
"github.com/kandev/kandev/internal/agent/mcpconfig"
)
type AgentProfileDTO struct {
ID string `json:"id"`
AgentID string `json:"agent_id"`
Name string `json:"name"`
AgentDisplayName string `json:"agent_display_name"`
Model string `json:"model"`
Mode string `json:"mode,omitempty"`
ConfigOptions map[string]string `json:"config_options,omitempty"`
AllowIndexing bool `json:"allow_indexing"` // Deprecated: use CLIFlags. Retained for legacy clients.
AutoApprove bool `json:"auto_approve"`
CLIFlags []CLIFlagDTO `json:"cli_flags"`
EnvVars []ProfileEnvVarDTO `json:"env_vars,omitempty"`
CLIPassthrough bool `json:"cli_passthrough"`
// CommandPrefix is an optional launcher prefix prepended to the agent
// command (e.g. "greywall --"). Shell-tokenised at launch time.
CommandPrefix string `json:"command_prefix,omitempty"`
UserModified bool `json:"user_modified"`
// WorkspaceID scopes the profile to an office workspace. Empty for
// shallow kanban-only profiles. Surfaced so consumers (e.g. test
// cleanup helpers) can distinguish office-owned profiles from
// kanban-only ones without a separate lookup.
WorkspaceID string `json:"workspace_id,omitempty"`
// BillingType is computed at read time from credential files — not stored in the DB.
// Values: "api_key" | "subscription". Empty for agents that don't support billing type detection.
BillingType string `json:"billing_type,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// CLIFlagDTO mirrors models.CLIFlag on the wire. Each entry is one user-facing
// CLI argument on a profile; at launch time the `flag` string is shell-split
// and only entries with `enabled:true` reach the agent subprocess argv.
type CLIFlagDTO struct {
Description string `json:"description"`
Flag string `json:"flag"`
Enabled bool `json:"enabled"`
}
// ProfileEnvVarDTO mirrors models.ProfileEnvVar on the wire.
type ProfileEnvVarDTO struct {
Key string `json:"key"`
Value string `json:"value,omitempty"`
SecretID string `json:"secret_id,omitempty"`
}
type TUIConfigDTO struct {
Command string `json:"command"`
DisplayName string `json:"display_name"`
Model string `json:"model,omitempty"`
Description string `json:"description,omitempty"`
CommandArgs []string `json:"command_args,omitempty"`
WaitForTerminal bool `json:"wait_for_terminal"`
}
type AgentDTO struct {
ID string `json:"id"`
Name string `json:"name"`
WorkspaceID *string `json:"workspace_id,omitempty"`
SupportsMCP bool `json:"supports_mcp"`
MCPConfigPath string `json:"mcp_config_path,omitempty"`
TUIConfig *TUIConfigDTO `json:"tui_config,omitempty"`
Profiles []AgentProfileDTO `json:"profiles"`
// CapabilityStatus mirrors the host utility probe status so clients can
// flag agents that need login or reinstallation without fetching the
// full model config separately. "" for agents that aren't probed
// (mock, tui-only).
CapabilityStatus string `json:"capability_status,omitempty"`
CapabilityError string `json:"capability_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListAgentsResponse struct {
Agents []AgentDTO `json:"agents"`
Total int `json:"total"`
}
type AgentDiscoveryDTO struct {
Name string `json:"name"`
SupportsMCP bool `json:"supports_mcp"`
MCPConfigPath string `json:"mcp_config_path,omitempty"`
InstallationPaths []string `json:"installation_paths,omitempty"`
Available bool `json:"available"`
MatchedPath string `json:"matched_path,omitempty"`
LoginCommand *LoginCommandDTO `json:"login_command,omitempty"`
}
// LoginCommandDTO describes an interactive login command surfaced to the UI.
// The frontend uses it to render a "Login" button that opens a PTY terminal
// running the named command.
type LoginCommandDTO struct {
Cmd []string `json:"cmd"`
Description string `json:"description,omitempty"`
}
type ListDiscoveryResponse struct {
Agents []AgentDiscoveryDTO `json:"agents"`
Total int `json:"total"`
}
type AgentCapabilitiesDTO struct {
SupportsSessionResume bool `json:"supports_session_resume"`
SupportsShell bool `json:"supports_shell"`
SupportsWorkspaceOnly bool `json:"supports_workspace_only"`
}
type ModelEntryDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Provider string `json:"provider"`
ContextWindow int `json:"context_window"`
IsDefault bool `json:"is_default"`
Source string `json:"source,omitempty"`
// Meta carries agent-specific extras from ACP's `_meta` field. For
// GitHub Copilot this includes `copilotUsage` (e.g. "1x", "0.33x",
// "0x" — the premium-request multiplier) and `copilotEnablement`.
Meta map[string]any `json:"meta,omitempty"`
}
type ModelConfigDTO struct {
DefaultModel string `json:"default_model"`
AvailableModels []ModelEntryDTO `json:"available_models"`
CurrentModelID string `json:"current_model_id,omitempty"`
AvailableModes []ModeEntryDTO `json:"available_modes,omitempty"`
CurrentModeID string `json:"current_mode_id,omitempty"`
AvailableCommands []CommandEntryDTO `json:"available_commands,omitempty"`
ConfigOptions []ConfigOptionDTO `json:"config_options,omitempty"`
SupportsDynamicModels bool `json:"supports_dynamic_models"`
// Status reflects the host utility probe state for this agent type:
// "probing" | "ok" | "auth_required" | "not_installed" | "failed".
Status string `json:"status,omitempty"`
Error string `json:"error,omitempty"`
}
type ConfigOptionDTO struct {
Type string `json:"type"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
CurrentValue string `json:"current_value"`
Category string `json:"category,omitempty"`
Options []ConfigOptionChoiceDTO `json:"options,omitempty"`
}
type ConfigOptionChoiceDTO struct {
Value string `json:"value"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
type PermissionSettingDTO struct {
Supported bool `json:"supported"`
Default bool `json:"default"`
Label string `json:"label"`
Description string `json:"description"`
ApplyMethod string `json:"apply_method,omitempty"`
CLIFlag string `json:"cli_flag,omitempty"`
CLIFlagValue string `json:"cli_flag_value,omitempty"`
}
type PassthroughConfigDTO struct {
Supported bool `json:"supported"`
Label string `json:"label"`
Description string `json:"description"`
AutoInjectPrompt bool `json:"auto_inject_prompt"`
SubmitSequence string `json:"submit_sequence"`
// MCPInjection is a short human-readable phrase describing how kandev
// injects MCP servers into this agent's CLI in passthrough mode (e.g.
// "an MCP config file passed via the --mcp-config flag"). Empty when the
// agent declares no MCP strategy.
MCPInjection string `json:"mcp_injection,omitempty"`
}
type ToolStatusDTO struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
Available bool `json:"available"`
MatchedPath string `json:"matched_path,omitempty"`
InstallScript string `json:"install_script,omitempty"`
Description string `json:"description,omitempty"`
InfoURL string `json:"info_url,omitempty"`
}
type AvailableAgentDTO struct {
Name string `json:"name"`
DisplayName string `json:"display_name"`
Description string `json:"description,omitempty"`
InstallScript string `json:"install_script,omitempty"`
SupportsMCP bool `json:"supports_mcp"`
MCPConfigPath string `json:"mcp_config_path,omitempty"`
InstallationPaths []string `json:"installation_paths,omitempty"`
Available bool `json:"available"`
MatchedPath string `json:"matched_path,omitempty"`
Capabilities AgentCapabilitiesDTO `json:"capabilities"`
ModelConfig ModelConfigDTO `json:"model_config"`
PermissionSettings map[string]PermissionSettingDTO `json:"permission_settings,omitempty"`
PassthroughConfig *PassthroughConfigDTO `json:"passthrough_config,omitempty"`
LoginCommand *LoginCommandDTO `json:"login_command,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListAvailableAgentsResponse struct {
Agents []AvailableAgentDTO `json:"agents"`
Tools []ToolStatusDTO `json:"tools,omitempty"`
Total int `json:"total"`
}
// InstallJobStatus represents the state of an install job.
type InstallJobStatus string
const (
InstallJobStatusQueued InstallJobStatus = "queued"
InstallJobStatusRunning InstallJobStatus = "running"
InstallJobStatusSucceeded InstallJobStatus = "succeeded"
InstallJobStatusFailed InstallJobStatus = "failed"
)
// InstallJobDTO is the snapshot of an install job returned via HTTP and
// broadcast via WS.
type InstallJobDTO struct {
JobID string `json:"job_id"`
Name string `json:"agent_name"`
Status InstallJobStatus `json:"status"`
Output string `json:"output,omitempty"`
Error string `json:"error,omitempty"`
ExitCode *int `json:"exit_code,omitempty"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
}
// EnqueueInstallResponse is returned by POST /agent-install/:agentName.
// The install runs asynchronously; clients subscribe to WS notifications
// (agent.install.started/output/finished) or poll GET /agent-install/jobs.
type EnqueueInstallResponse struct {
JobID string `json:"job_id"`
}
// ListInstallJobsResponse wraps the snapshot list for the jobs endpoint.
type ListInstallJobsResponse struct {
Jobs []InstallJobDTO `json:"jobs"`
}
type AgentProfileMcpConfigDTO struct {
ProfileID string `json:"profile_id"`
Enabled bool `json:"enabled"`
Servers map[string]mcpconfig.ServerDef `json:"servers"`
Meta map[string]any `json:"meta,omitempty"`
}
// CommandPreviewRequest is the request body for previewing the agent CLI command
type CommandPreviewRequest struct {
Model string `json:"model"`
PermissionSettings map[string]bool `json:"permission_settings"`
CLIPassthrough bool `json:"cli_passthrough"`
CommandPrefix string `json:"command_prefix,omitempty"`
}
// CommandPreviewResponse is the response for the command preview endpoint
type CommandPreviewResponse struct {
Supported bool `json:"supported"`
Command []string `json:"command"`
CommandString string `json:"command_string"`
}
// DynamicModelsResponse is the response for the /agent-models/:agentName endpoint.
// Data now comes from the host utility capability cache populated by ACP probes.
type DynamicModelsResponse struct {
AgentName string `json:"agent_name"`
Status string `json:"status"` // "probing" | "ok" | "auth_required" | "not_installed" | "failed"
Models []ModelEntryDTO `json:"models"`
CurrentModelID string `json:"current_model_id,omitempty"`
Modes []ModeEntryDTO `json:"modes,omitempty"`
CurrentModeID string `json:"current_mode_id,omitempty"`
Commands []CommandEntryDTO `json:"commands,omitempty"`
Error *string `json:"error"`
}
// ModeEntryDTO is a single ACP session mode advertised by an agent.
type ModeEntryDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
// CommandEntryDTO is a slash command advertised by the agent.
type CommandEntryDTO struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}