Skip to content
Open
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
1 change: 0 additions & 1 deletion .infer/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ a2a:
ttl: 300
task:
status_poll_seconds: 5
idle_timeout_sec: 60
polling_strategy: exponential
initial_poll_interval_sec: 2
max_poll_interval_sec: 60
Expand Down
1 change: 0 additions & 1 deletion CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ a2a:
# Task monitoring configuration
task:
status_poll_seconds: 5
idle_timeout_sec: 60
polling_strategy: "exponential"
initial_poll_interval_sec: 2
max_poll_interval_sec: 60
Expand Down
1 change: 0 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ func initConfig() {
v.SetDefault("a2a.cache.enabled", defaults.A2A.Cache.Enabled)
v.SetDefault("a2a.cache.ttl", defaults.A2A.Cache.TTL)
v.SetDefault("a2a.task.status_poll_seconds", defaults.A2A.Task.StatusPollSeconds)
v.SetDefault("a2a.task.idle_timeout_sec", defaults.A2A.Task.IdleTimeoutSec)
v.SetDefault("a2a.task.polling_strategy", defaults.A2A.Task.PollingStrategy)
v.SetDefault("a2a.task.initial_poll_interval_sec", defaults.A2A.Task.InitialPollIntervalSec)
v.SetDefault("a2a.task.max_poll_interval_sec", defaults.A2A.Task.MaxPollIntervalSec)
Expand Down
2 changes: 0 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@ type A2AAgentInfo struct {
// A2ATaskConfig contains configuration for A2A task processing
type A2ATaskConfig struct {
StatusPollSeconds int `yaml:"status_poll_seconds" mapstructure:"status_poll_seconds"`
IdleTimeoutSec int `yaml:"idle_timeout_sec" mapstructure:"idle_timeout_sec"`
PollingStrategy string `yaml:"polling_strategy" mapstructure:"polling_strategy"`
InitialPollIntervalSec int `yaml:"initial_poll_interval_sec" mapstructure:"initial_poll_interval_sec"`
MaxPollIntervalSec int `yaml:"max_poll_interval_sec" mapstructure:"max_poll_interval_sec"`
Expand Down Expand Up @@ -643,7 +642,6 @@ Respond with ONLY the title, no quotes or explanation.`,
},
Task: A2ATaskConfig{
StatusPollSeconds: 5,
IdleTimeoutSec: 60,
PollingStrategy: "exponential",
InitialPollIntervalSec: 2,
MaxPollIntervalSec: 60,
Expand Down
110 changes: 11 additions & 99 deletions internal/services/tools/a2a_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,14 @@ type A2ASubmitTaskTool struct {

// A2ASubmitTaskResult represents the result of an A2A task operation
type A2ASubmitTaskResult struct {
TaskID string `json:"task_id"`
ContextID string `json:"context_id,omitempty"`
AgentURL string `json:"agent_url"`
State string `json:"state"`
Message string `json:"message"`
TaskResult string `json:"task_result,omitempty"`
Success bool `json:"success"`
WentIdle bool `json:"went_idle,omitempty"`
IdleTimeout time.Duration `json:"idle_timeout,omitempty"`
Task *adk.Task `json:"task,omitempty"`
TaskID string `json:"task_id"`
ContextID string `json:"context_id,omitempty"`
AgentURL string `json:"agent_url"`
State string `json:"state"`
Message string `json:"message"`
TaskResult string `json:"task_result,omitempty"`
Success bool `json:"success"`
Task *adk.Task `json:"task,omitempty"`
}

// NewA2ASubmitTaskTool creates a new A2A task tool
Expand Down Expand Up @@ -291,13 +289,8 @@ func (t *A2ASubmitTaskTool) pollTaskInBackground(
adkClient := t.getOrCreateClient(agentURL)

strategy := t.config.A2A.Task.PollingStrategy
if strategy == "immediate_idle" {
t.handleImmediateIdle(agentURL, taskID, state)
t.stopPolling(taskID)
return
}

currentInterval, deadline := t.initializePollingStrategy(agentURL, taskID, strategy)
currentInterval := t.initializePollingStrategy(agentURL, taskID, strategy)
state.CurrentInterval = currentInterval
state.NextPollTime = time.Now().Add(currentInterval)

Expand All @@ -321,16 +314,6 @@ func (t *A2ASubmitTaskTool) pollTaskInBackground(
pollingDetails.WriteString(fmt.Sprintf("Poll #%d: interval=%v, elapsed=%v\n",
pollAttempt, currentInterval, time.Since(state.StartedAt)))

shouldStop, stopResult := t.checkIdleConditions(agentURL, taskID, strategy, currentInterval, deadline, pollAttempt, state, pollingDetails.String())
if shouldStop {
if stopResult != nil && state.ResultChan != nil {
state.ResultChan <- stopResult
time.Sleep(100 * time.Millisecond)
}
t.stopPolling(taskID)
return
}

state.LastPollAt = time.Now()

currentTask, err := t.queryTask(ctx, adkClient, taskID)
Expand Down Expand Up @@ -378,28 +361,7 @@ func (t *A2ASubmitTaskTool) getOrCreateClient(agentURL string) client.A2AClient
return client.NewClient(agentURL)
}

func (t *A2ASubmitTaskTool) handleImmediateIdle(agentURL, taskID string, state *domain.TaskPollingState) {
idleTimeout := time.Duration(t.config.A2A.Task.IdleTimeoutSec) * time.Second
result := &domain.ToolExecutionResult{
ToolName: "A2A_SubmitTask",
Success: true,
Duration: time.Since(state.StartedAt),
Data: A2ASubmitTaskResult{
TaskID: taskID,
AgentURL: agentURL,
State: string(adk.TaskStateWorking),
Success: true,
Message: "Task delegated and went idle immediately",
WentIdle: true,
IdleTimeout: idleTimeout,
},
}
if state.ResultChan != nil {
state.ResultChan <- result
}
}

func (t *A2ASubmitTaskTool) initializePollingStrategy(agentURL, taskID, strategy string) (time.Duration, time.Time) {
func (t *A2ASubmitTaskTool) initializePollingStrategy(agentURL, taskID, strategy string) time.Duration {
var currentInterval time.Duration

if strategy == "exponential" {
Expand All @@ -408,53 +370,7 @@ func (t *A2ASubmitTaskTool) initializePollingStrategy(agentURL, taskID, strategy
currentInterval = time.Duration(t.config.A2A.Task.StatusPollSeconds) * time.Second
}

idleTimeout := time.Duration(t.config.A2A.Task.IdleTimeoutSec) * time.Second
deadline := time.Now().Add(idleTimeout)

return currentInterval, deadline
}

func (t *A2ASubmitTaskTool) checkIdleConditions(agentURL, taskID, strategy string, currentInterval time.Duration, deadline time.Time, pollAttempt int, state *domain.TaskPollingState, pollingDetails string) (bool, *domain.ToolExecutionResult) {
idleTimeout := time.Duration(t.config.A2A.Task.IdleTimeoutSec) * time.Second

if strategy == "exponential" {
maxInterval := time.Duration(t.config.A2A.Task.MaxPollIntervalSec) * time.Second
if currentInterval >= maxInterval {
result := &domain.ToolExecutionResult{
ToolName: "A2A_SubmitTask",
Success: true,
Duration: time.Since(state.StartedAt),
Data: A2ASubmitTaskResult{
TaskID: taskID,
AgentURL: agentURL,
State: string(adk.TaskStateWorking),
Success: true,
Message: fmt.Sprintf("Task went idle after reaching max poll interval of %v", maxInterval),
WentIdle: true,
IdleTimeout: idleTimeout,
},
}
return true, result
}
} else if time.Now().After(deadline) {
result := &domain.ToolExecutionResult{
ToolName: "A2A_SubmitTask",
Success: true,
Duration: time.Since(state.StartedAt),
Data: A2ASubmitTaskResult{
TaskID: taskID,
AgentURL: agentURL,
State: string(adk.TaskStateWorking),
Success: true,
Message: fmt.Sprintf("Task went idle after %v", idleTimeout),
WentIdle: true,
IdleTimeout: idleTimeout,
},
}
return true, result
}

return false, nil
return currentInterval
}

func (t *A2ASubmitTaskTool) queryTask(ctx context.Context, adkClient client.A2AClient, taskID string) (*adk.Task, error) {
Expand Down Expand Up @@ -690,10 +606,6 @@ func (t *A2ASubmitTaskTool) FormatPreview(result *domain.ToolExecutionResult) st
preview = data.Message
}

if data.WentIdle {
return fmt.Sprintf("%s (went idle)", preview)
}

return preview
}

Expand Down
2 changes: 0 additions & 2 deletions internal/services/tools/a2a_task_error_handling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ func TestA2ASubmitTaskTool_MultipleAgents(t *testing.T) {
Enabled: true,
Task: config.A2ATaskConfig{
StatusPollSeconds: 1,
IdleTimeoutSec: 5,
},
Tools: config.A2AToolsConfig{
SubmitTask: config.SubmitTaskToolConfig{
Expand Down Expand Up @@ -427,7 +426,6 @@ func TestA2ASubmitTaskTool_NoExistingTask(t *testing.T) {
Enabled: true,
Task: config.A2ATaskConfig{
StatusPollSeconds: 1,
IdleTimeoutSec: 5,
},
Tools: config.A2AToolsConfig{
SubmitTask: config.SubmitTaskToolConfig{
Expand Down
3 changes: 0 additions & 3 deletions internal/ui/components/tool_call_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,6 @@ func (r *ToolCallRenderer) renderToolCallContent(toolInfo ToolInfo, arguments, s
case "executed", "completed", "complete":
statusIcon = icons.CheckMark
statusText = status
case "idle":
statusIcon = icons.CheckMark
statusText = "delegated"
case "error", "failed":
statusIcon = icons.CrossMark
statusText = status
Expand Down