From a8c341a2d5fed731ddf44bd4796f0f9b84820c30 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Thu, 16 Apr 2026 14:43:32 -0700 Subject: [PATCH 1/5] Add askUser config, disable askUser by default Signed-off-by: Dobes Vandermeer --- go/api/adk/types.go | 8 ++ go/api/v1alpha2/agent_types.go | 12 +++ .../controller/reconciler/reconciler.go | 5 ++ .../agent/adk_api_translator_test.go | 81 +++++++++++++++++++ .../controller/translator/agent/compiler.go | 6 ++ .../kagent-adk/src/kagent/adk/types.py | 11 ++- .../kagent-adk/tests/unittests/test_types.py | 42 ++++++++++ 7 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 python/packages/kagent-adk/tests/unittests/test_types.py diff --git a/go/api/adk/types.go b/go/api/adk/types.go index b9f939384..c412e54ce 100644 --- a/go/api/adk/types.go +++ b/go/api/adk/types.go @@ -448,6 +448,11 @@ func (c *AgentCompressionConfig) UnmarshalJSON(data []byte) error { return nil } +// AskUserConfig configures the "ask user" tool. +type AskUserConfig struct { + Enabled bool `json:"enabled"` +} + // See `python/packages/kagent-adk/src/kagent/adk/types.py` for the python version of this type AgentConfig struct { Model Model `json:"model"` @@ -461,6 +466,7 @@ type AgentConfig struct { Memory *MemoryConfig `json:"memory,omitempty"` Network *NetworkConfig `json:"network,omitempty"` ContextConfig *AgentContextConfig `json:"context_config,omitempty"` + AskUser *AskUserConfig `json:"ask_user,omitempty"` } // GetStream returns the stream value or default if not set @@ -492,6 +498,7 @@ func (a *AgentConfig) UnmarshalJSON(data []byte) error { Memory json.RawMessage `json:"memory"` Network *NetworkConfig `json:"network,omitempty"` ContextConfig *AgentContextConfig `json:"context_config,omitempty"` + AskUser *AskUserConfig `json:"ask_user,omitempty"` } if err := json.Unmarshal(data, &tmp); err != nil { return err @@ -521,6 +528,7 @@ func (a *AgentConfig) UnmarshalJSON(data []byte) error { a.Memory = memory a.Network = tmp.Network a.ContextConfig = tmp.ContextConfig + a.AskUser = tmp.AskUser return nil } diff --git a/go/api/v1alpha2/agent_types.go b/go/api/v1alpha2/agent_types.go index f19b0c3f4..a89b5bc70 100644 --- a/go/api/v1alpha2/agent_types.go +++ b/go/api/v1alpha2/agent_types.go @@ -82,6 +82,18 @@ type AgentSpec struct { // See: https://gateway-api.sigs.k8s.io/guides/multiple-ns/#cross-namespace-routing // +optional AllowedNamespaces *AllowedNamespaces `json:"allowedNamespaces,omitempty"` + + // AskUser configures the "ask user" tool for this agent. + // When enabled, the agent can pause execution and ask the user for input. + // +optional + AskUser *AskUserSpec `json:"askUser,omitempty"` +} + +// AskUserSpec configures the "ask user" tool for an agent. +type AskUserSpec struct { + // Enabled indicates whether the "ask user" tool should be enabled for this agent. + // +kubebuilder:validation:Required + Enabled bool `json:"enabled"` } // +kubebuilder:validation:AtLeastOneOf=refs,gitRefs diff --git a/go/core/internal/controller/reconciler/reconciler.go b/go/core/internal/controller/reconciler/reconciler.go index 212e8c431..2e6d3ce82 100644 --- a/go/core/internal/controller/reconciler/reconciler.go +++ b/go/core/internal/controller/reconciler/reconciler.go @@ -807,6 +807,11 @@ func (a *kagentReconciler) validateRuntimeFeatures(agent v1alpha2.AgentObject) s unsupported = append(unsupported, "context compression/compaction (not implemented in Go runtime)") } + // AskUser: Not yet implemented in Go runtime + if spec.AskUser != nil && spec.AskUser.Enabled { + unsupported = append(unsupported, "ask user (not implemented in Go runtime)") + } + if len(unsupported) == 0 { return "" } diff --git a/go/core/internal/controller/translator/agent/adk_api_translator_test.go b/go/core/internal/controller/translator/agent/adk_api_translator_test.go index 6f0dbb80f..74eef692f 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator_test.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator_test.go @@ -1413,3 +1413,84 @@ func Test_AdkApiTranslator_SandboxAgent_BYOEmitsSandbox(t *testing.T) { require.False(t, sawDeploy) require.False(t, sawService, "sandbox runtime must not include Service; agent-sandbox owns it") } + +func Test_AdkApiTranslator_AskUser(t *testing.T) { + scheme := schemev1.Scheme + require.NoError(t, v1alpha2.AddToScheme(scheme)) + + modelConfig := &v1alpha2.ModelConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-model", + Namespace: "default", + }, + Spec: v1alpha2.ModelConfigSpec{ + Model: "gpt-4", + Provider: v1alpha2.ModelProviderOpenAI, + }, + } + + makeAgent := func(askUser *v1alpha2.AskUserSpec) *v1alpha2.Agent { + return &v1alpha2.Agent{ + ObjectMeta: metav1.ObjectMeta{Name: "test-agent", Namespace: "default"}, + Spec: v1alpha2.AgentSpec{ + Type: v1alpha2.AgentType_Declarative, + Description: "Test agent", + Declarative: &v1alpha2.DeclarativeAgentSpec{ + SystemMessage: "You are a test agent", + ModelConfig: "test-model", + }, + AskUser: askUser, + }, + } + } + + tests := []struct { + name string + agent *v1alpha2.Agent + assertConfig func(t *testing.T, cfg *adk.AgentConfig) + }{ + { + name: "ask user disabled", + agent: makeAgent(&v1alpha2.AskUserSpec{Enabled: false}), + assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { + require.NotNil(t, cfg.AskUser) + assert.False(t, cfg.AskUser.Enabled) + }, + }, + { + name: "ask user enabled", + agent: makeAgent(&v1alpha2.AskUserSpec{Enabled: true}), + assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { + require.NotNil(t, cfg.AskUser) + assert.True(t, cfg.AskUser.Enabled) + }, + }, + { + name: "ask user not specified", + agent: makeAgent(nil), + assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { + assert.Nil(t, cfg.AskUser) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kubeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(modelConfig.DeepCopy()). + Build() + + defaultModel := types.NamespacedName{Namespace: "default", Name: "test-model"} + trans := translator.NewAdkApiTranslator(kubeClient, defaultModel, nil, "", nil) + outputs, err := translator.TranslateAgent(context.Background(), trans, tt.agent) + + require.NoError(t, err) + require.NotNil(t, outputs) + require.NotNil(t, outputs.Config) + if tt.assertConfig != nil { + tt.assertConfig(t, outputs.Config) + } + }) + } +} diff --git a/go/core/internal/controller/translator/agent/compiler.go b/go/core/internal/controller/translator/agent/compiler.go index 0232a859e..d31a84160 100644 --- a/go/core/internal/controller/translator/agent/compiler.go +++ b/go/core/internal/controller/translator/agent/compiler.go @@ -176,6 +176,12 @@ func (a *adkApiTranslator) translateInlineAgent(ctx context.Context, agent v1alp Stream: new(spec.Declarative.Stream), } + if spec.AskUser != nil { + cfg.AskUser = &adk.AskUserConfig{ + Enabled: spec.AskUser.Enabled, + } + } + if spec.Sandbox != nil && spec.Sandbox.Network != nil { cfg.Network = &adk.NetworkConfig{ AllowedDomains: append([]string(nil), spec.Sandbox.Network.AllowedDomains...), diff --git a/python/packages/kagent-adk/src/kagent/adk/types.py b/python/packages/kagent-adk/src/kagent/adk/types.py index f31609b53..3baefc1dc 100644 --- a/python/packages/kagent-adk/src/kagent/adk/types.py +++ b/python/packages/kagent-adk/src/kagent/adk/types.py @@ -273,6 +273,12 @@ class NetworkConfig(BaseModel): allowed_domains: list[str] = Field(default_factory=list) +class AskUserConfig(BaseModel): + """Ask user tool configuration.""" + + enabled: bool + + class AgentConfig(BaseModel): model: ModelUnion = Field(discriminator="type") description: str @@ -285,6 +291,7 @@ class AgentConfig(BaseModel): memory: MemoryConfig | None = None # Memory configuration network: NetworkConfig | None = None context_config: ContextConfig | None = None + ask_user: AskUserConfig | None = None def to_agent(self, name: str, sts_integration: Optional[ADKTokenPropagationPlugin] = None) -> Agent: if name is None or not str(name).strip(): @@ -400,8 +407,8 @@ async def rewrite_url_to_proxy(request: httpx.Request) -> None: code_executor = SandboxedLocalCodeExecutor() if self.execute_code else None model = _create_llm_from_model_config(self.model) - # Add built-in ask_user tool unconditionally — every agent can ask the user questions. - tools.append(AskUserTool()) + if self.ask_user and self.ask_user.enabled: + tools.append(AskUserTool()) # Build before_tool_callback if any tools require approval before_tool_callback = make_approval_callback(tools_requiring_approval) if tools_requiring_approval else None diff --git a/python/packages/kagent-adk/tests/unittests/test_types.py b/python/packages/kagent-adk/tests/unittests/test_types.py new file mode 100644 index 000000000..e10fa41bf --- /dev/null +++ b/python/packages/kagent-adk/tests/unittests/test_types.py @@ -0,0 +1,42 @@ +from kagent.adk.types import AgentConfig, AskUserConfig, GeminiVertexAI + + +def test_ask_user_enabled(): + """Verify that AskUserTool is added when ask_user.enabled is true.""" + config = AgentConfig( + model=GeminiVertexAI(model="gemini-pro"), + description="Test Agent", + instruction="You are a test agent.", + ask_user=AskUserConfig(enabled=True), + ) + agent = config.to_agent(name="test-agent") + assert any( + tool.name == "ask_user" for tool in agent.tools + ), "AskUserTool should be present when enabled" + + +def test_ask_user_disabled(): + """Verify that AskUserTool is not added when ask_user.enabled is false.""" + config = AgentConfig( + model=GeminiVertexAI(model="gemini-pro"), + description="Test Agent", + instruction="You are a test agent.", + ask_user=AskUserConfig(enabled=False), + ) + agent = config.to_agent(name="test-agent") + assert not any( + tool.name == "ask_user" for tool in agent.tools + ), "AskUserTool should not be present when disabled" + + +def test_ask_user_not_specified(): + """Verify that AskUserTool is not added when ask_user is not specified.""" + config = AgentConfig( + model=GeminiVertexAI(model="gemini-pro"), + description="Test Agent", + instruction="You are a test agent.", + ) + agent = config.to_agent(name="test-agent") + assert not any( + tool.name == "ask_user" for tool in agent.tools + ), "AskUserTool should not be present when not specified" From d70f894323a2240822d3c8cee43447e1fbd196ac Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 21 Apr 2026 19:44:10 -0700 Subject: [PATCH 2/5] Update CRDs Signed-off-by: Dobes Vandermeer --- .../config/crd/bases/kagent.dev_agents.yaml | 12 +++++++++++ .../crd/bases/kagent.dev_sandboxagents.yaml | 12 +++++++++++ go/api/v1alpha2/zz_generated.deepcopy.go | 20 +++++++++++++++++++ .../templates/kagent.dev_agents.yaml | 12 +++++++++++ .../templates/kagent.dev_sandboxagents.yaml | 12 +++++++++++ 5 files changed, 68 insertions(+) diff --git a/go/api/config/crd/bases/kagent.dev_agents.yaml b/go/api/config/crd/bases/kagent.dev_agents.yaml index 8180fda2d..95b7c703e 100644 --- a/go/api/config/crd/bases/kagent.dev_agents.yaml +++ b/go/api/config/crd/bases/kagent.dev_agents.yaml @@ -2472,6 +2472,18 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' + askUser: + description: |- + AskUser configures the "ask user" tool for this agent. + When enabled, the agent can pause execution and ask the user for input. + properties: + enabled: + description: Enabled indicates whether the "ask user" tool should + be enabled for this agent. + type: boolean + required: + - enabled + type: object byo: properties: deployment: diff --git a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml index 9118e971b..297de500b 100644 --- a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml +++ b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml @@ -122,6 +122,18 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' + askUser: + description: |- + AskUser configures the "ask user" tool for this agent. + When enabled, the agent can pause execution and ask the user for input. + properties: + enabled: + description: Enabled indicates whether the "ask user" tool should + be enabled for this agent. + type: boolean + required: + - enabled + type: object byo: properties: deployment: diff --git a/go/api/v1alpha2/zz_generated.deepcopy.go b/go/api/v1alpha2/zz_generated.deepcopy.go index 9c5377240..70e4369a5 100644 --- a/go/api/v1alpha2/zz_generated.deepcopy.go +++ b/go/api/v1alpha2/zz_generated.deepcopy.go @@ -175,6 +175,11 @@ func (in *AgentSpec) DeepCopyInto(out *AgentSpec) { *out = new(AllowedNamespaces) (*in).DeepCopyInto(*out) } + if in.AskUser != nil { + in, out := &in.AskUser, &out.AskUser + *out = new(AskUserSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentSpec. @@ -260,6 +265,21 @@ func (in *AnthropicVertexAIConfig) DeepCopy() *AnthropicVertexAIConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AskUserSpec) DeepCopyInto(out *AskUserSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AskUserSpec. +func (in *AskUserSpec) DeepCopy() *AskUserSpec { + if in == nil { + return nil + } + out := new(AskUserSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureOpenAIConfig) DeepCopyInto(out *AzureOpenAIConfig) { *out = *in diff --git a/helm/kagent-crds/templates/kagent.dev_agents.yaml b/helm/kagent-crds/templates/kagent.dev_agents.yaml index 8180fda2d..95b7c703e 100644 --- a/helm/kagent-crds/templates/kagent.dev_agents.yaml +++ b/helm/kagent-crds/templates/kagent.dev_agents.yaml @@ -2472,6 +2472,18 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' + askUser: + description: |- + AskUser configures the "ask user" tool for this agent. + When enabled, the agent can pause execution and ask the user for input. + properties: + enabled: + description: Enabled indicates whether the "ask user" tool should + be enabled for this agent. + type: boolean + required: + - enabled + type: object byo: properties: deployment: diff --git a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml index 9118e971b..297de500b 100644 --- a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml +++ b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml @@ -122,6 +122,18 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' + askUser: + description: |- + AskUser configures the "ask user" tool for this agent. + When enabled, the agent can pause execution and ask the user for input. + properties: + enabled: + description: Enabled indicates whether the "ask user" tool should + be enabled for this agent. + type: boolean + required: + - enabled + type: object byo: properties: deployment: From cdf60d08d112bc861cabc7a330bad370307b2053 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 21 Apr 2026 19:48:56 -0700 Subject: [PATCH 3/5] Fix tests Signed-off-by: Dobes Vandermeer --- .../kagent-adk/tests/unittests/test_types.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/packages/kagent-adk/tests/unittests/test_types.py b/python/packages/kagent-adk/tests/unittests/test_types.py index e10fa41bf..f9eb7529a 100644 --- a/python/packages/kagent-adk/tests/unittests/test_types.py +++ b/python/packages/kagent-adk/tests/unittests/test_types.py @@ -4,12 +4,12 @@ def test_ask_user_enabled(): """Verify that AskUserTool is added when ask_user.enabled is true.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro"), + model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ask_user=AskUserConfig(enabled=True), ) - agent = config.to_agent(name="test-agent") + agent = config.to_agent(name="test_ask_user_enabled") assert any( tool.name == "ask_user" for tool in agent.tools ), "AskUserTool should be present when enabled" @@ -18,12 +18,12 @@ def test_ask_user_enabled(): def test_ask_user_disabled(): """Verify that AskUserTool is not added when ask_user.enabled is false.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro"), + model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ask_user=AskUserConfig(enabled=False), ) - agent = config.to_agent(name="test-agent") + agent = config.to_agent(name="test_ask_user_disabled") assert not any( tool.name == "ask_user" for tool in agent.tools ), "AskUserTool should not be present when disabled" @@ -32,11 +32,11 @@ def test_ask_user_disabled(): def test_ask_user_not_specified(): """Verify that AskUserTool is not added when ask_user is not specified.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro"), + model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ) - agent = config.to_agent(name="test-agent") + agent = config.to_agent(name="test_ask_user_not_specified") assert not any( tool.name == "ask_user" for tool in agent.tools ), "AskUserTool should not be present when not specified" From 308c482e04703ae000b5b876dd175e44c3c727e9 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 21 Apr 2026 20:04:36 -0700 Subject: [PATCH 4/5] Reformat Signed-off-by: Dobes Vandermeer --- .../kagent-adk/tests/unittests/test_types.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/python/packages/kagent-adk/tests/unittests/test_types.py b/python/packages/kagent-adk/tests/unittests/test_types.py index f9eb7529a..6a973c4e6 100644 --- a/python/packages/kagent-adk/tests/unittests/test_types.py +++ b/python/packages/kagent-adk/tests/unittests/test_types.py @@ -4,39 +4,35 @@ def test_ask_user_enabled(): """Verify that AskUserTool is added when ask_user.enabled is true.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), + model=GeminiVertexAI(model="gemini-pro", type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ask_user=AskUserConfig(enabled=True), ) agent = config.to_agent(name="test_ask_user_enabled") - assert any( - tool.name == "ask_user" for tool in agent.tools - ), "AskUserTool should be present when enabled" + assert any(tool.name == "ask_user" for tool in agent.tools), "AskUserTool should be present when enabled" def test_ask_user_disabled(): """Verify that AskUserTool is not added when ask_user.enabled is false.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), + model=GeminiVertexAI(model="gemini-pro", type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ask_user=AskUserConfig(enabled=False), ) agent = config.to_agent(name="test_ask_user_disabled") - assert not any( - tool.name == "ask_user" for tool in agent.tools - ), "AskUserTool should not be present when disabled" + assert not any(tool.name == "ask_user" for tool in agent.tools), "AskUserTool should not be present when disabled" def test_ask_user_not_specified(): """Verify that AskUserTool is not added when ask_user is not specified.""" config = AgentConfig( - model=GeminiVertexAI(model="gemini-pro",type="gemini_vertex_ai"), + model=GeminiVertexAI(model="gemini-pro", type="gemini_vertex_ai"), description="Test Agent", instruction="You are a test agent.", ) agent = config.to_agent(name="test_ask_user_not_specified") - assert not any( - tool.name == "ask_user" for tool in agent.tools - ), "AskUserTool should not be present when not specified" + assert not any(tool.name == "ask_user" for tool in agent.tools), ( + "AskUserTool should not be present when not specified" + ) From 03a089d8400fdf851342e08afa04da34bbb8009d Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Wed, 22 Apr 2026 16:43:32 -0700 Subject: [PATCH 5/5] Move askUser enabled into builtinTools map Signed-off-by: Dobes Vandermeer --- .../config/crd/bases/kagent.dev_agents.yaml | 22 +++++----- .../crd/bases/kagent.dev_sandboxagents.yaml | 22 +++++----- go/api/v1alpha2/agent_types.go | 20 +++++----- go/api/v1alpha2/zz_generated.deepcopy.go | 40 +++++++++---------- .../controller/reconciler/reconciler.go | 2 +- .../agent/adk_api_translator_test.go | 10 ++--- .../controller/translator/agent/compiler.go | 4 +- .../templates/kagent.dev_agents.yaml | 22 +++++----- .../templates/kagent.dev_sandboxagents.yaml | 22 +++++----- 9 files changed, 78 insertions(+), 86 deletions(-) diff --git a/go/api/config/crd/bases/kagent.dev_agents.yaml b/go/api/config/crd/bases/kagent.dev_agents.yaml index 95b7c703e..022069799 100644 --- a/go/api/config/crd/bases/kagent.dev_agents.yaml +++ b/go/api/config/crd/bases/kagent.dev_agents.yaml @@ -2472,18 +2472,6 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' - askUser: - description: |- - AskUser configures the "ask user" tool for this agent. - When enabled, the agent can pause execution and ask the user for input. - properties: - enabled: - description: Enabled indicates whether the "ask user" tool should - be enabled for this agent. - type: boolean - required: - - enabled - type: object byo: properties: deployment: @@ -6212,6 +6200,16 @@ spec: minItems: 1 type: array type: object + builtinTools: + description: BuiltinTools configures the built-in tools available + to this agent. + properties: + askUser: + description: |- + AskUser enables the "ask user" tool. + When true, the agent can pause execution and ask the user for input. + type: boolean + type: object context: description: |- Context configures context management for this agent. diff --git a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml index 297de500b..93119a45f 100644 --- a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml +++ b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml @@ -122,18 +122,6 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' - askUser: - description: |- - AskUser configures the "ask user" tool for this agent. - When enabled, the agent can pause execution and ask the user for input. - properties: - enabled: - description: Enabled indicates whether the "ask user" tool should - be enabled for this agent. - type: boolean - required: - - enabled - type: object byo: properties: deployment: @@ -3862,6 +3850,16 @@ spec: minItems: 1 type: array type: object + builtinTools: + description: BuiltinTools configures the built-in tools available + to this agent. + properties: + askUser: + description: |- + AskUser enables the "ask user" tool. + When true, the agent can pause execution and ask the user for input. + type: boolean + type: object context: description: |- Context configures context management for this agent. diff --git a/go/api/v1alpha2/agent_types.go b/go/api/v1alpha2/agent_types.go index a89b5bc70..c7a64aea9 100644 --- a/go/api/v1alpha2/agent_types.go +++ b/go/api/v1alpha2/agent_types.go @@ -82,18 +82,14 @@ type AgentSpec struct { // See: https://gateway-api.sigs.k8s.io/guides/multiple-ns/#cross-namespace-routing // +optional AllowedNamespaces *AllowedNamespaces `json:"allowedNamespaces,omitempty"` - - // AskUser configures the "ask user" tool for this agent. - // When enabled, the agent can pause execution and ask the user for input. - // +optional - AskUser *AskUserSpec `json:"askUser,omitempty"` } -// AskUserSpec configures the "ask user" tool for an agent. -type AskUserSpec struct { - // Enabled indicates whether the "ask user" tool should be enabled for this agent. - // +kubebuilder:validation:Required - Enabled bool `json:"enabled"` +// BuiltinToolsSpec configures the built-in tools available to a declarative agent. +type BuiltinToolsSpec struct { + // AskUser enables the "ask user" tool. + // When true, the agent can pause execution and ask the user for input. + // +optional + AskUser bool `json:"askUser,omitempty"` } // +kubebuilder:validation:AtLeastOneOf=refs,gitRefs @@ -221,6 +217,10 @@ type DeclarativeAgentSpec struct { // This includes event compaction (compression) and context caching. // +optional Context *ContextConfig `json:"context,omitempty"` + + // BuiltinTools configures the built-in tools available to this agent. + // +optional + BuiltinTools *BuiltinToolsSpec `json:"builtinTools,omitempty"` } // SandboxConfig configures sandboxed execution behavior. diff --git a/go/api/v1alpha2/zz_generated.deepcopy.go b/go/api/v1alpha2/zz_generated.deepcopy.go index 70e4369a5..8c4fb9aaa 100644 --- a/go/api/v1alpha2/zz_generated.deepcopy.go +++ b/go/api/v1alpha2/zz_generated.deepcopy.go @@ -175,11 +175,6 @@ func (in *AgentSpec) DeepCopyInto(out *AgentSpec) { *out = new(AllowedNamespaces) (*in).DeepCopyInto(*out) } - if in.AskUser != nil { - in, out := &in.AskUser, &out.AskUser - *out = new(AskUserSpec) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentSpec. @@ -265,21 +260,6 @@ func (in *AnthropicVertexAIConfig) DeepCopy() *AnthropicVertexAIConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AskUserSpec) DeepCopyInto(out *AskUserSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AskUserSpec. -func (in *AskUserSpec) DeepCopy() *AskUserSpec { - if in == nil { - return nil - } - out := new(AskUserSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureOpenAIConfig) DeepCopyInto(out *AzureOpenAIConfig) { *out = *in @@ -355,6 +335,21 @@ func (in *BedrockConfig) DeepCopy() *BedrockConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BuiltinToolsSpec) DeepCopyInto(out *BuiltinToolsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BuiltinToolsSpec. +func (in *BuiltinToolsSpec) DeepCopy() *BuiltinToolsSpec { + if in == nil { + return nil + } + out := new(BuiltinToolsSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ByoDeploymentSpec) DeepCopyInto(out *ByoDeploymentSpec) { *out = *in @@ -515,6 +510,11 @@ func (in *DeclarativeAgentSpec) DeepCopyInto(out *DeclarativeAgentSpec) { *out = new(ContextConfig) (*in).DeepCopyInto(*out) } + if in.BuiltinTools != nil { + in, out := &in.BuiltinTools, &out.BuiltinTools + *out = new(BuiltinToolsSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeclarativeAgentSpec. diff --git a/go/core/internal/controller/reconciler/reconciler.go b/go/core/internal/controller/reconciler/reconciler.go index 2e6d3ce82..fc1266624 100644 --- a/go/core/internal/controller/reconciler/reconciler.go +++ b/go/core/internal/controller/reconciler/reconciler.go @@ -808,7 +808,7 @@ func (a *kagentReconciler) validateRuntimeFeatures(agent v1alpha2.AgentObject) s } // AskUser: Not yet implemented in Go runtime - if spec.AskUser != nil && spec.AskUser.Enabled { + if decl.BuiltinTools != nil && decl.BuiltinTools.AskUser { unsupported = append(unsupported, "ask user (not implemented in Go runtime)") } diff --git a/go/core/internal/controller/translator/agent/adk_api_translator_test.go b/go/core/internal/controller/translator/agent/adk_api_translator_test.go index 74eef692f..1ac34d6cd 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator_test.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator_test.go @@ -1429,7 +1429,7 @@ func Test_AdkApiTranslator_AskUser(t *testing.T) { }, } - makeAgent := func(askUser *v1alpha2.AskUserSpec) *v1alpha2.Agent { + makeAgent := func(builtinTools *v1alpha2.BuiltinToolsSpec) *v1alpha2.Agent { return &v1alpha2.Agent{ ObjectMeta: metav1.ObjectMeta{Name: "test-agent", Namespace: "default"}, Spec: v1alpha2.AgentSpec{ @@ -1438,8 +1438,8 @@ func Test_AdkApiTranslator_AskUser(t *testing.T) { Declarative: &v1alpha2.DeclarativeAgentSpec{ SystemMessage: "You are a test agent", ModelConfig: "test-model", + BuiltinTools: builtinTools, }, - AskUser: askUser, }, } } @@ -1451,7 +1451,7 @@ func Test_AdkApiTranslator_AskUser(t *testing.T) { }{ { name: "ask user disabled", - agent: makeAgent(&v1alpha2.AskUserSpec{Enabled: false}), + agent: makeAgent(&v1alpha2.BuiltinToolsSpec{AskUser: false}), assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { require.NotNil(t, cfg.AskUser) assert.False(t, cfg.AskUser.Enabled) @@ -1459,14 +1459,14 @@ func Test_AdkApiTranslator_AskUser(t *testing.T) { }, { name: "ask user enabled", - agent: makeAgent(&v1alpha2.AskUserSpec{Enabled: true}), + agent: makeAgent(&v1alpha2.BuiltinToolsSpec{AskUser: true}), assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { require.NotNil(t, cfg.AskUser) assert.True(t, cfg.AskUser.Enabled) }, }, { - name: "ask user not specified", + name: "builtin tools not specified", agent: makeAgent(nil), assertConfig: func(t *testing.T, cfg *adk.AgentConfig) { assert.Nil(t, cfg.AskUser) diff --git a/go/core/internal/controller/translator/agent/compiler.go b/go/core/internal/controller/translator/agent/compiler.go index d31a84160..e8e7d106a 100644 --- a/go/core/internal/controller/translator/agent/compiler.go +++ b/go/core/internal/controller/translator/agent/compiler.go @@ -176,9 +176,9 @@ func (a *adkApiTranslator) translateInlineAgent(ctx context.Context, agent v1alp Stream: new(spec.Declarative.Stream), } - if spec.AskUser != nil { + if spec.Declarative.BuiltinTools != nil { cfg.AskUser = &adk.AskUserConfig{ - Enabled: spec.AskUser.Enabled, + Enabled: spec.Declarative.BuiltinTools.AskUser, } } diff --git a/helm/kagent-crds/templates/kagent.dev_agents.yaml b/helm/kagent-crds/templates/kagent.dev_agents.yaml index 95b7c703e..022069799 100644 --- a/helm/kagent-crds/templates/kagent.dev_agents.yaml +++ b/helm/kagent-crds/templates/kagent.dev_agents.yaml @@ -2472,18 +2472,6 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' - askUser: - description: |- - AskUser configures the "ask user" tool for this agent. - When enabled, the agent can pause execution and ask the user for input. - properties: - enabled: - description: Enabled indicates whether the "ask user" tool should - be enabled for this agent. - type: boolean - required: - - enabled - type: object byo: properties: deployment: @@ -6212,6 +6200,16 @@ spec: minItems: 1 type: array type: object + builtinTools: + description: BuiltinTools configures the built-in tools available + to this agent. + properties: + askUser: + description: |- + AskUser enables the "ask user" tool. + When true, the agent can pause execution and ask the user for input. + type: boolean + type: object context: description: |- Context configures context management for this agent. diff --git a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml index 297de500b..93119a45f 100644 --- a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml +++ b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml @@ -122,18 +122,6 @@ spec: x-kubernetes-validations: - message: selector must be specified when from is Selector rule: '!(self.from == ''Selector'' && !has(self.selector))' - askUser: - description: |- - AskUser configures the "ask user" tool for this agent. - When enabled, the agent can pause execution and ask the user for input. - properties: - enabled: - description: Enabled indicates whether the "ask user" tool should - be enabled for this agent. - type: boolean - required: - - enabled - type: object byo: properties: deployment: @@ -3862,6 +3850,16 @@ spec: minItems: 1 type: array type: object + builtinTools: + description: BuiltinTools configures the built-in tools available + to this agent. + properties: + askUser: + description: |- + AskUser enables the "ask user" tool. + When true, the agent can pause execution and ask the user for input. + type: boolean + type: object context: description: |- Context configures context management for this agent.