Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ AgentTeams does not compete with other Agent runtimes. Instead of implementing A

## News

- **2026-07-17**: [Release Notes](https://github.com/agentscope-ai/AgentTeams/releases/tag/v1.2.0-beta.1) — AgentTeams v1.2.0-beta.1 (prerelease): completes the public rename from HiClaw across images, Kubernetes APIs, Helm, Matrix, storage, and runtime contracts; adds the plugin platform, TeamHarness and WorkerFlow integrations, Matrix AppService and Human SSO, model-provider routing and LLM preflight, plus richer controller observability. Beta installation requires explicit opt-in, while the stable default remains v1.1.2.
- **2026-07-17**: [Release Notes](https://github.com/agentscope-ai/AgentTeams/releases/tag/v1.2.0-beta.1) — AgentTeams v1.2.0-beta.1 (prerelease): completes the public rename from the retired predecessor across images, Kubernetes APIs, Helm, Matrix, storage, and runtime contracts; adds the plugin platform, TeamHarness and WorkerFlow integrations, Matrix AppService and Human SSO, model-provider routing and LLM preflight, plus richer controller observability. Beta installation requires explicit opt-in, while the stable default remains v1.1.2.
- **2026-05-27**: [Release Notes](https://github.com/agentscope-ai/AgentTeams/releases/tag/v1.1.2) — AgentTeams v1.1.2: QwenPaw-first installer with keep-all upgrade flow, Team human coordinators and refreshed Team Leader coordination tools, Nacos remote skills with `sts-agentteams` / `ai-registry` STS scope, Worker CR-name decoupled from runtime name, controller reconcile metrics and graceful shutdown.
- **2026-05-07**: [Release Notes](https://github.com/agentscope-ai/AgentTeams/releases/tag/v1.1.1) | [Changelog](changelog/v1.1.1.md) — AgentTeams v1.1.1: declarative MCP on Worker/Manager/Team CRDs (breaking) and on Team Leader, custom `spec.env` for CRs, Token Plan + Qwen Cloud international + `qwen3.6-plus`, namespace-scoped controller RBAC, optional `SOUL.md` in Worker packages.
- **2026-04-24**: [English](blog/agentteams-1.1.0-release.md) | [中文](blog/zh-cn/agentteams-1.1.0-release.md) — AgentTeams v1.1.0: Kubernetes-native control plane, Hermes autonomous coding agent runtime, 1.7 GB image shrink, agt CLI replaces shell scripts.
Expand Down
132 changes: 4 additions & 128 deletions agentteams-controller/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const (
LabelManager = "agentteams.io/manager"
LabelRole = "agentteams.io/role"
LabelRuntime = "agentteams.io/runtime"
LabelTeam = "agentteams.io/team"
)

// LabelWorkerSvcName records the ClusterIP Service name created for a
Expand Down Expand Up @@ -383,7 +382,7 @@ type WorkerStatus struct {

// BackendRuntime records the backend type currently used for this worker's container.
// Set after successful creation or backend switch.
// Values: "pod" (default), or "" (unset = migration, treated as spec default).
// Values: "pod" (default), or "" before the first successful deployment.
// Only meaningful in incluster mode; Docker mode leaves this empty.
BackendRuntime string `json:"backendRuntime,omitempty"`

Expand Down Expand Up @@ -427,6 +426,7 @@ type TeamSpec struct {
// WorkerMembers references existing Worker CRs as team members.
// The TeamReconciler validates membership, provisions rooms, injects
// runtime context, and aggregates member status from these references.
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=128
WorkerMembers []TeamWorkerRef `json:"workerMembers,omitempty"`

Expand All @@ -438,15 +438,6 @@ type TeamSpec struct {
// Worker's openclaw.json and coordination context AGENTS.md.
// Example: "30m". Empty means leader heartbeat is disabled.
HeartbeatEvery string `json:"heartbeatEvery,omitempty"`

// Deprecated: Leader defines the team leader's runtime configuration.
// Retained for backward compatibility during migration. Ignored when
// WorkerMembers is non-empty.
Leader LeaderSpec `json:"leader,omitempty"`
// Deprecated: Workers defines team worker runtime configurations.
// Retained for backward compatibility during migration. Ignored when
// WorkerMembers is non-empty.
Workers []TeamWorkerSpec `json:"workers,omitempty"`
}

// TeamWorkerRef references an existing Worker CR as a team member.
Expand All @@ -455,8 +446,8 @@ type TeamWorkerRef struct {
// +kubebuilder:validation:MaxLength=253
Name string `json:"name"`
// Role is this member's role within the team: "team_leader" or "worker".
// Empty defaults to "worker".
Role string `json:"role,omitempty"`
// +kubebuilder:validation:Enum=team_leader;worker
Role string `json:"role"`
}

func (s TeamSpec) EffectiveTeamName(metadataName string) string {
Expand All @@ -477,121 +468,6 @@ type TeamMemberSpec struct {
Role string `json:"role,omitempty"` // coordinator (default)
}

type LeaderSpec struct {
Name string `json:"name"`
WorkerName string `json:"workerName,omitempty"`
Model string `json:"model,omitempty"`
ModelProvider string `json:"modelProvider,omitempty"` // APIG Model API name for per-leader LLM provider
Runtime string `json:"runtime,omitempty"`
Image string `json:"image,omitempty"`
Identity string `json:"identity,omitempty"`
Soul string `json:"soul,omitempty"`
Agents string `json:"agents,omitempty"`
Package string `json:"package,omitempty"`
RemoteSkills []RemoteSkillSource `json:"remoteSkills,omitempty"` // remote skills from source registries
McpServers []MCPServer `json:"mcpServers,omitempty"`
Heartbeat *TeamLeaderHeartbeatSpec `json:"heartbeat,omitempty"`
WorkerIdleTimeout string `json:"workerIdleTimeout,omitempty"`
ChannelPolicy *ChannelPolicySpec `json:"channelPolicy,omitempty"`
State *string `json:"state,omitempty"` // desired lifecycle state: Running, Sleeping, Stopped
Resources *AgentResourceRequirements `json:"resources,omitempty"`

// AccessEntries declares the cloud permissions this leader should be
// granted via agentteams-credential-provider. See AccessEntry for semantics.
// When empty the controller applies team-member defaults (agents/<name>/*
// + shared/* + teams/<team>/* on the configured bucket).
AccessEntries []AccessEntry `json:"accessEntries,omitempty"`

// DeployMode specifies where the leader pod runs.
// "Local" (default): created in the controller's own cluster.
// "Edge": externally hosted outside the controller's managed pod path.
DeployMode *string `json:"deployMode,omitempty"`

// ServiceEnabled controls whether a ClusterIP Service is created
// alongside the leader pod (same cluster, namespace, name).
ServiceEnabled *bool `json:"serviceEnabled,omitempty"`

// Env holds user-defined environment variables injected into the
// leader container. See WorkerSpec.Env for the collision policy.
Env map[string]string `json:"env,omitempty"`

// Labels are user-defined Pod labels stamped onto the leader Pod.
// Merged on top of Team.metadata.labels and below controller system
// labels (see WorkerSpec.Labels godoc). omitempty preserves zero-value
// wire compatibility for callers that never set this field.
Labels map[string]string `json:"labels,omitempty"`
}

type TeamLeaderHeartbeatSpec struct {
Enabled bool `json:"enabled,omitempty"`
Every string `json:"every,omitempty"`
}

type TeamWorkerSpec struct {
Name string `json:"name"`
WorkerName string `json:"workerName,omitempty"`
Model string `json:"model,omitempty"`
ModelProvider string `json:"modelProvider,omitempty"` // APIG Model API name for per-worker LLM provider
Runtime string `json:"runtime,omitempty"`
Image string `json:"image,omitempty"`
Identity string `json:"identity,omitempty"`
Soul string `json:"soul,omitempty"`
Agents string `json:"agents,omitempty"`
Skills []string `json:"skills,omitempty"`
RemoteSkills []RemoteSkillSource `json:"remoteSkills,omitempty"` // remote skills from source registries
McpServers []MCPServer `json:"mcpServers,omitempty"`
Package string `json:"package,omitempty"`
Expose []ExposePort `json:"expose,omitempty"`
ChannelPolicy *ChannelPolicySpec `json:"channelPolicy,omitempty"`
IdleTimeout string `json:"idleTimeout,omitempty"`
State *string `json:"state,omitempty"` // desired lifecycle state: Running, Sleeping, Stopped
Resources *AgentResourceRequirements `json:"resources,omitempty"`

// AccessEntries declares the cloud permissions this team worker should be
// granted via agentteams-credential-provider. See AccessEntry for semantics.
// When empty the controller applies team-member defaults (agents/<name>/*
// + shared/* + teams/<team>/* on the configured bucket).
AccessEntries []AccessEntry `json:"accessEntries,omitempty"`

// DeployMode specifies where the team worker pod runs.
// "Local" (default): created in the controller's own cluster.
// "Edge": externally hosted outside the controller's managed pod path.
DeployMode *string `json:"deployMode,omitempty"`

// ServiceEnabled controls whether a ClusterIP Service is created
// alongside the team worker pod (same cluster, namespace, name).
ServiceEnabled *bool `json:"serviceEnabled,omitempty"`

// Env holds user-defined environment variables injected into this
// team worker's container. See WorkerSpec.Env for the collision policy.
Env map[string]string `json:"env,omitempty"`

// Labels are user-defined Pod labels stamped onto this team worker's
// Pod. Merged on top of Team.metadata.labels and below controller
// system labels (see WorkerSpec.Labels godoc). omitempty preserves
// zero-value wire compatibility for callers that never set this field.
Labels map[string]string `json:"labels,omitempty"`
}

// EffectiveWorkerName returns the runtime identity key for a team leader.
// Empty workerName falls back to spec.name supplied by caller.
func (s LeaderSpec) EffectiveWorkerName() string {
if s.WorkerName != "" {
return s.WorkerName
}
return s.Name

}

// EffectiveWorkerName returns the runtime identity key for a team worker.
// Empty workerName falls back to spec.name supplied by caller.
func (s TeamWorkerSpec) EffectiveWorkerName() string {
if s.WorkerName != "" {
return s.WorkerName
}
return s.Name
}

type TeamStatus struct {
Phase string `json:"phase,omitempty"` // Pending/Active/Degraded/Failed
TeamRoomID string `json:"teamRoomID,omitempty"`
Expand Down
98 changes: 0 additions & 98 deletions agentteams-controller/api/v1beta1/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,68 +161,6 @@ func TestManagerSpec_DeepCopyLabels(t *testing.T) {
}
}

// TestLeaderSpec_DeepCopyLabels verifies LeaderSpec.Labels survives
// DeepCopy. The Leader path is exercised separately from TeamWorkerSpec
// because TeamSpec.DeepCopyInto calls LeaderSpec.DeepCopyInto directly
// and any regression there would silently drop leader labels.
func TestLeaderSpec_DeepCopyLabels(t *testing.T) {
src := LeaderSpec{Name: "ld", Labels: map[string]string{"role-hint": "planner"}}
cp := *src.DeepCopy()
if !reflect.DeepEqual(cp.Labels, src.Labels) {
t.Fatalf("copy labels=%v want %v", cp.Labels, src.Labels)
}
src.Labels["role-hint"] = "mutated"
if cp.Labels["role-hint"] != "planner" {
t.Fatalf("DeepCopy aliased LeaderSpec.Labels: %v", cp.Labels)
}
}

// TestTeamWorkerSpec_DeepCopyLabels verifies TeamWorkerSpec.Labels
// survives DeepCopy through the TeamSpec.Workers[] slice path.
func TestTeamWorkerSpec_DeepCopyLabels(t *testing.T) {
src := TeamWorkerSpec{Name: "w1", Labels: map[string]string{"skill": "rust"}}
cp := *src.DeepCopy()
if !reflect.DeepEqual(cp.Labels, src.Labels) {
t.Fatalf("copy labels=%v want %v", cp.Labels, src.Labels)
}
src.Labels["skill"] = "mutated"
if cp.Labels["skill"] != "rust" {
t.Fatalf("DeepCopy aliased TeamWorkerSpec.Labels: %v", cp.Labels)
}
}

// TestTeamSpec_DeepCopyCascadesToMemberLabels verifies that DeepCopy on
// the top-level TeamSpec correctly cascades into both LeaderSpec.Labels
// and every TeamWorkerSpec.Labels — catching the case where a future
// refactor regenerates zz_generated.deepcopy.go and accidentally drops a
// nested call.
func TestTeamSpec_DeepCopyCascadesToMemberLabels(t *testing.T) {
src := TeamSpec{
Leader: LeaderSpec{
Name: "ld",
Labels: map[string]string{"role-hint": "planner"},
},
Workers: []TeamWorkerSpec{
{Name: "w1", Labels: map[string]string{"skill": "rust"}},
{Name: "w2"}, // nil labels branch
},
}
cp := *src.DeepCopy()

src.Leader.Labels["role-hint"] = "mutated"
src.Workers[0].Labels["skill"] = "mutated"

if cp.Leader.Labels["role-hint"] != "planner" {
t.Fatalf("LeaderSpec.Labels not isolated: %v", cp.Leader.Labels)
}
if cp.Workers[0].Labels["skill"] != "rust" {
t.Fatalf("Workers[0].Labels not isolated: %v", cp.Workers[0].Labels)
}
if cp.Workers[1].Labels != nil {
t.Fatalf("Workers[1].Labels should remain nil after DeepCopy, got %v", cp.Workers[1].Labels)
}
}

func TestWorkerSpec_DeepCopyResources(t *testing.T) {
src := WorkerSpec{
Model: "m",
Expand Down Expand Up @@ -263,39 +201,3 @@ func TestManagerSpec_DeepCopyResources(t *testing.T) {
t.Fatalf("DeepCopy aliased ManagerSpec.Resources: %v", cp.Resources)
}
}

func TestTeamSpec_DeepCopyCascadesToMemberResources(t *testing.T) {
src := TeamSpec{
Leader: LeaderSpec{
Name: "lead",
Resources: &AgentResourceRequirements{
Requests: AgentResourceValues{CPU: "300m", Memory: "768Mi"},
Limits: AgentResourceValues{CPU: "2", Memory: "3Gi"},
},
},
Workers: []TeamWorkerSpec{
{
Name: "w1",
Resources: &AgentResourceRequirements{
Requests: AgentResourceValues{CPU: "200m", Memory: "512Mi"},
Limits: AgentResourceValues{CPU: "1", Memory: "2Gi"},
},
},
{Name: "w2"},
},
}
cp := *src.DeepCopy()

src.Leader.Resources.Requests.CPU = "999m"
src.Workers[0].Resources.Limits.Memory = "9Gi"

if cp.Leader.Resources.Requests.CPU != "300m" {
t.Fatalf("LeaderSpec.Resources not isolated: %v", cp.Leader.Resources)
}
if cp.Workers[0].Resources.Limits.Memory != "2Gi" {
t.Fatalf("TeamWorkerSpec.Resources not isolated: %v", cp.Workers[0].Resources)
}
if cp.Workers[1].Resources != nil {
t.Fatalf("Workers[1].Resources should remain nil after DeepCopy, got %v", cp.Workers[1].Resources)
}
}
Loading
Loading