package rpc import ( "context" "errors" "strings" agentpb "git.ipao.vip/rogee/go-sip/gen/agent" "git.ipao.vip/rogee/go-sip/internal/agent" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // ApplyApprovedTaskControl applies a task-scoped control only for the active // Dispatcher session. It deliberately has no external control command ID or // revision CAS; repeated deliveries execute the control again. func (s *Server) ApplyApprovedTaskControl(ctx context.Context, req *agentpb.ApplyApprovedTaskControlRequest) (*agentpb.ApplyApprovedTaskControlResponse, error) { if s == nil || ctx == nil || req == nil || req.Meta == nil { return nil, status.Error(codes.InvalidArgument, "approved task control requires a context and session metadata") } if err := s.authorize(ctx, req.Meta); err != nil { return nil, err } dispatcherID, err := s.sessions.ApprovedDispatcher(req.Meta, s.now()) if err != nil { return nil, err } if req.DispatcherId != dispatcherID { return nil, status.Error(codes.PermissionDenied, "Dispatcher does not own the active Agent session") } if s.mode != "mock" && s.mode != "nonprod-real" { return nil, status.Error(codes.FailedPrecondition, "approved task control requires an approved call mode") } if req.TenantId <= 0 || strings.TrimSpace(req.TaskId) == "" { return nil, status.Error(codes.InvalidArgument, "approved task control identity is incomplete") } var action string switch req.Action { case agentpb.ControlAction_CONTROL_ACTION_PAUSE: action = "pause" case agentpb.ControlAction_CONTROL_ACTION_STOP: action = "stop" case agentpb.ControlAction_CONTROL_ACTION_RESUME: action = "resume" default: return nil, status.Error(codes.InvalidArgument, "approved task control action is invalid") } var policy string switch req.ActiveCallPolicy { case agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_HANGUP: policy = "hangup" case agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_DRAIN: policy = "drain" case agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_UNSPECIFIED: default: return nil, status.Error(codes.InvalidArgument, "approved task control policy is invalid") } if (action == "resume" && policy != "") || (action != "resume" && policy == "") { return nil, status.Error(codes.InvalidArgument, "approved task control policy does not match action") } if s.approvedTaskCalls == nil { return nil, status.Error(codes.FailedPrecondition, "Agent task-control adapter is unavailable") } if err := s.approvedTaskCalls.Apply(ctx, agent.TaskIdentity{DispatcherID: dispatcherID, TenantID: req.TenantId, TaskID: req.TaskId}, action, policy); err != nil { switch { case errors.Is(err, context.DeadlineExceeded): return nil, status.Error(codes.DeadlineExceeded, "Agent task control timed out before calls ended") case errors.Is(err, context.Canceled): return nil, status.Error(codes.Canceled, "Agent task control interrupted before calls ended") default: return nil, status.Error(codes.FailedPrecondition, "Agent task control was not applied") } } return &agentpb.ApplyApprovedTaskControlResponse{Accepted: true}, nil }