Persist validated_only shadow plans without executing, gate elevated tiers behind INTENT_PLAN_PROJECT_ALLOWLIST, and keep legacy chat routing for shadow.
64 lines
1.4 KiB
Go
64 lines
1.4 KiB
Go
package intentflow
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// AllowAllProjects is the wildcard sentinel for INTENT_PLAN_PROJECT_ALLOWLIST=*.
|
|
const AllowAllProjects int64 = -1
|
|
|
|
// ParseProjectAllowlist parses a comma-separated project ID list.
|
|
// Empty string → deny elevated tiers. "*" → allow all. Invalid tokens are skipped.
|
|
func ParseProjectAllowlist(raw string) []int64 {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return nil
|
|
}
|
|
if raw == "*" {
|
|
return []int64{AllowAllProjects}
|
|
}
|
|
parts := strings.Split(raw, ",")
|
|
out := make([]int64, 0, len(parts))
|
|
seen := map[int64]struct{}{}
|
|
for _, p := range parts {
|
|
p = strings.TrimSpace(p)
|
|
if p == "" || p == "*" {
|
|
if p == "*" {
|
|
return []int64{AllowAllProjects}
|
|
}
|
|
continue
|
|
}
|
|
id, err := strconv.ParseInt(p, 10, 64)
|
|
if err != nil || id <= 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
out = append(out, id)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ProjectAllowed reports whether elevated plan tiers may act for projectID.
|
|
// Legacy tier always returns true (no allowlist gate). Empty allowlist denies.
|
|
func (f FeatureFlags) ProjectAllowed(projectID int64) bool {
|
|
if f.PlanTier == TierLegacy || f.PlanTier == "" {
|
|
return true
|
|
}
|
|
if len(f.ProjectAllowlist) == 0 {
|
|
return false
|
|
}
|
|
for _, id := range f.ProjectAllowlist {
|
|
if id == AllowAllProjects {
|
|
return true
|
|
}
|
|
if id == projectID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|