mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-23 10:08:38 +02:00
* fix(quick-create): bound dialog height + scroll editor when content overflows Pasting a screenshot into the agent-create prompt expanded the editor unbounded, which dragged DialogContent past the viewport since the agent mode className had no max-height. Manual mode was unaffected because manualDialogContentClass pins `!h-96`. - Cap agent-mode DialogContent at `!max-h-[80vh]` (width stays `!max-w-xl`); short prompts still render compact, tall content stops at 80% of the viewport. - Switch the editor wrapper to `flex-1 min-h-[140px] overflow-y-auto` so it absorbs the remaining vertical space inside the now-bounded DialogContent and scrolls internally instead of pushing the dialog. * feat(quick-create): gate on daemon CLI version with pre-check + server enforcement The agent-create flow depends on multica CLI behavior introduced in v0.2.20 (URL attachment handling, no-retry semantics on `multica issue create` failure — see PR #1851 / MUL-1496). Older daemons either double-create issues on partial CLI failures or mishandle pasted screenshot URLs. Per J's review on MUL-1496, gate the flow at two layers — frontend pre-check for fast feedback, server re-check as the trust boundary, both fail-closed on missing/unparsable versions. Server: - New MinQuickCreateCLIVersion + CheckMinCLIVersion helper in pkg/agent (with sentinel errors for missing vs too-old). - QuickCreateIssue handler reads runtime metadata.cli_version and returns a stable 422 { code: "daemon_version_unsupported", current_version, min_version, runtime_id } before enqueuing. - The check runs after the existing online + ownership validation, so all rejections surface uniformly through the modal's existing error path. Frontend: - New @multica/core/runtimes/cli-version with the min version constant, parser, and runtime-metadata reader (tiny semver, no new lib dep). - AgentCreatePanel resolves the selected agent's runtime, runs the same check, shows an inline amber notice below the agent picker when missing/too old, and disables the Create button. - Submit handler also catches the server's 422 (defensive race — runtime can re-register between pre-check and submit) and surfaces the same wording in the error row. Switching to manual create remains a clean escape hatch — manual mode doesn't talk to a daemon at all, so an outdated CLI doesn't block the user from filing the issue.
111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
package agent
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// MinVersions defines the minimum required CLI version for each agent type.
|
|
// Versions below these will be rejected during daemon registration.
|
|
var MinVersions = map[string]string{
|
|
"claude": "2.0.0",
|
|
"codex": "0.100.0", // app-server --listen stdio:// added in 0.100.0
|
|
"copilot": "1.0.0", // --output-format json envelope stable from 1.0.x
|
|
}
|
|
|
|
// MinQuickCreateCLIVersion gates the agent-create (quick-create) flow against
|
|
// the multica CLI version reported by the daemon at registration time. The
|
|
// quick-create prompt that the agent runs depends on CLI behavior introduced
|
|
// after this version (attachment URL handling, no-retry semantics on
|
|
// `multica issue create` failure — see PR #1851); older daemons would either
|
|
// double-create issues or mishandle pasted screenshot URLs. Treated as a hard
|
|
// requirement: missing / unparsable / below this threshold all fail closed.
|
|
const MinQuickCreateCLIVersion = "0.2.20"
|
|
|
|
// Errors returned by CheckMinCLIVersion. Callers branch on these to surface
|
|
// "needs upgrade" vs "version not reported" with the right user message.
|
|
var (
|
|
ErrCLIVersionMissing = errors.New("multica CLI version not reported by daemon")
|
|
ErrCLIVersionTooOld = errors.New("multica CLI version is below required minimum")
|
|
)
|
|
|
|
// CheckMinCLIVersion returns nil when `detected` parses as ≥ minimum. Returns
|
|
// ErrCLIVersionMissing for empty or unparsable input, and ErrCLIVersionTooOld
|
|
// when parsable but below the minimum. The caller can check for these
|
|
// sentinel errors with errors.Is to drive the response shape.
|
|
func CheckMinCLIVersion(detected string) error {
|
|
d := strings.TrimSpace(detected)
|
|
if d == "" {
|
|
return ErrCLIVersionMissing
|
|
}
|
|
parsed, err := parseSemver(d)
|
|
if err != nil {
|
|
return ErrCLIVersionMissing
|
|
}
|
|
min, err := parseSemver(MinQuickCreateCLIVersion)
|
|
if err != nil {
|
|
// Misconfiguration in the constant itself — fail closed as missing.
|
|
return ErrCLIVersionMissing
|
|
}
|
|
if parsed.lessThan(min) {
|
|
return ErrCLIVersionTooOld
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// semver holds a parsed semantic version (major.minor.patch).
|
|
type semver struct {
|
|
Major, Minor, Patch int
|
|
}
|
|
|
|
// versionRe matches version strings like "2.1.100", "v2.0.0", or
|
|
// "2.1.100 (Claude Code)" — it extracts the first three numeric components.
|
|
var versionRe = regexp.MustCompile(`v?(\d+)\.(\d+)\.(\d+)`)
|
|
|
|
// parseSemver extracts a semver from a version string.
|
|
func parseSemver(raw string) (semver, error) {
|
|
m := versionRe.FindStringSubmatch(raw)
|
|
if m == nil {
|
|
return semver{}, fmt.Errorf("cannot parse version %q", raw)
|
|
}
|
|
major, _ := strconv.Atoi(m[1])
|
|
minor, _ := strconv.Atoi(m[2])
|
|
patch, _ := strconv.Atoi(m[3])
|
|
return semver{Major: major, Minor: minor, Patch: patch}, nil
|
|
}
|
|
|
|
// lessThan returns true if v < other.
|
|
func (v semver) lessThan(other semver) bool {
|
|
if v.Major != other.Major {
|
|
return v.Major < other.Major
|
|
}
|
|
if v.Minor != other.Minor {
|
|
return v.Minor < other.Minor
|
|
}
|
|
return v.Patch < other.Patch
|
|
}
|
|
|
|
// CheckMinVersion validates that detectedVersion meets the minimum for agentType.
|
|
// Returns nil if the version is acceptable or no minimum is defined.
|
|
func CheckMinVersion(agentType, detectedVersion string) error {
|
|
minRaw, ok := MinVersions[agentType]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
min, err := parseSemver(minRaw)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid minimum version %q for %s: %w", minRaw, agentType, err)
|
|
}
|
|
detected, err := parseSemver(detectedVersion)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse detected %s version %q: %w", agentType, detectedVersion, err)
|
|
}
|
|
if detected.lessThan(min) {
|
|
return fmt.Errorf("%s version %s is below minimum required %s — please upgrade", agentType, detectedVersion, minRaw)
|
|
}
|
|
return nil
|
|
}
|